Functions and imports

fileprefix = "20201221_"

ref_file_mm10 = "~/Documents/reference_data/mouse/ensembl/ensembl_gene_length_mm10.txt.gz"

# This is required to set the fonts of the paper plots to Arial
library(extrafont)
font_import(prompt=FALSE)
loadfonts()

library(RColorBrewer)
library(Seurat)
library(scater)
library(cowplot)
library(dplyr)
library(tidyr)
library(SoupX)

calc_tpm = function(dat, ref_file) {
  mm_annot = read.table(ref_file, header=T, stringsAsFactors=F, sep="\t")
  eff_length = mm_annot$eff_length[match(rownames(dat), mm_annot$mgi_symbol)]
  eff_length[!is.finite(eff_length)] = 1
  
  sce = SingleCellExperiment(assays=list(counts=as.matrix(dat), logcounts=log2(as.matrix(dat)+1)))
  tpm = calculateTPM(sce, eff_length)
  return(tpm)
}

theme_publication_plot = function(p, legend_title, legend_aes=4) {
  p = p +
  theme_cowplot() + 
  guides(fill = guide_legend(title=legend_title,
                              override.aes = list(size=legend_aes)),
         colour = guide_legend(title=legend_title,
                              override.aes = list(size=legend_aes))) +
  theme(axis.text = element_text(family="Arial", colour="black",size=10,face="plain"),
                       axis.title = element_text(family="Arial", colour="black",size=12,face="plain"),
                       strip.text.x = element_text(family="Arial", colour="black",size=12,face="plain", angle=90),
                       strip.text.y = element_text(family="Arial", colour="black",size=12,face="plain", angle=360),
                       strip.background = element_blank(),
                       panel.spacing.x = unit(1, "mm"),
                       panel.spacing.y = unit(3, "mm"),
                       plot.title = element_text(family="Arial", colour="black",size=12,face="plain",hjust = 0.5))
  return(p)
}

Load the data

samplenames = c("EA_WT_1", "EA_WT_2", "EA_NOTCH1_HOM_1", "EA_NOTCH1_HOM_2")
library_names = c("WT 1", "WT 2", "HOM KO 1", "HOM KO 2")
prefixes = c("c1_", "c2_", "s1_", "s2_")
expr_matrices = list()
celltypes_all = list()
for (i in 1:length(samplenames)) {
  prefix = prefixes[i]
  in_path = file.path("../cellranger/", samplenames[i])
  celltypes = read.table(file.path("../identify_celltypes", samplenames[i], "celltypes_per_cell.txt"), header=T, stringsAsFactors=F)
  sc = load10X(in_path)
  
  out = sc$toc
  colnames(out) = paste0(prefix, colnames(out))
  celltypes$cell = paste0(prefix, celltypes$cell)
  
  expr_matrices[[length(expr_matrices)+1]] = out
  celltypes_all[[length(celltypes_all)+1]] = celltypes
}

Read in some CellRanger stats

stats = list()
for (i in 1:length(samplenames)) {
  stats_sample = read.table(file.path("../cellranger/", samplenames[i], "metrics_summary.csv"), sep=",", header=T, stringsAsFactors=F)
  stats_sample$library = samplenames[i]
  stats[[samplenames[i]]] = stats_sample
}
stats = do.call(rbind, stats)
print(stats)

Explore to identify filters

nFeature_RNA_min = 2500
nFeature_RNA_max = 6500
nCount_RNA_max = 55000
prop.mt_min = 0.03
prop.mt_max = 0.1

seu = list()
for (i in 1:length(expr_matrices)) {
  seu[[i]] = CreateSeuratObject(counts = expr_matrices[[i]], min.cells = 0, min.features = 0)
  
  mito.genes = rownames(GetAssayData(object=seu[[i]]))[grepl(pattern = "^MT-", x = toupper(rownames(GetAssayData(object=seu[[i]]))))]
  percent.mito = Matrix::colSums(GetAssayData(object=seu[[i]], slot="counts")[mito.genes, ]) / Matrix::colSums(GetAssayData(object=seu[[i]], slot="counts"))
  seu[[i]] = AddMetaData(object = seu[[i]], metadata = percent.mito, col.name = "prop.mt")
  
  # plot prop.mt vs nFeature_RNA and vs nCount_RNA
  p1 = ggplot(seu[[i]]@meta.data) + aes(x=prop.mt, y=nFeature_RNA) + geom_point(size=0.1, colour="red") + scale_y_log10() + theme_cowplot() + geom_vline(xintercept=c(prop.mt_min, prop.mt_max), linetype=2) + geom_hline(yintercept=c(nFeature_RNA_min, nFeature_RNA_max), linetype=2)
  
  p2 = ggplot(seu[[i]]@meta.data) + aes(x=prop.mt, y=nCount_RNA) + geom_point(size=0.1, colour="blue") + scale_y_log10() + theme_cowplot() + geom_vline(xintercept=c(prop.mt_min, prop.mt_max), linetype=2) + geom_hline(yintercept=c(nCount_RNA_max), linetype=2)
  
  print(plot_grid(p1, p2))
  
  p1 = VlnPlot(seu[[i]], group.by="orig.ident", features = c( "nFeature_RNA"), ncol = 1, pt.size=0) +
    geom_hline(yintercept = c(nFeature_RNA_min, nFeature_RNA_max), linetype=2) + theme(legend.position="none")
  p2 = VlnPlot(seu[[i]], group.by="orig.ident", features = c( "nCount_RNA"), ncol = 1, pt.size=0) +
    geom_hline(yintercept = c(nCount_RNA_max), linetype=2) + theme(legend.position="none")
  p3 = VlnPlot(seu[[i]], group.by="orig.ident", features = c( "prop.mt"), ncol = 1, pt.size=0) + theme(legend.position="none") + geom_hline(yintercept = c(prop.mt_max, prop.mt_min), linetype=2)
  
  # if (grepl("KO", samplenames[i], fixed=T)) {
  #   p3 = p3 + geom_hline(yintercept = c(0.1, 0.002), linetype=2)
  # } else {
  #   p3 = p3 + geom_hline(yintercept = c(0.1, 0.01), linetype=2)
  # }
  
  print(plot_grid(p1, p2, p3, ncol=3))
}

All cells

Apply batch correction - MT filters and adjust for MT, nGenes and cell cycle

First adjustments per library

seu = list()
for (i in 1:length(expr_matrices)) {
  seu[[i]] = CreateSeuratObject(counts = expr_matrices[[i]], min.cells = 30, min.features = 2500)
  
  mito.genes = rownames(GetAssayData(object=seu[[i]]))[grepl(pattern = "^MT-", x = toupper(rownames(GetAssayData(object=seu[[i]]))))]
  percent.mito = Matrix::colSums(GetAssayData(object=seu[[i]], slot="counts")[mito.genes, ]) / Matrix::colSums(GetAssayData(object=seu[[i]], slot="counts"))
  seu[[i]] = AddMetaData(object = seu[[i]], metadata = percent.mito, col.name = "prop.mt")
  
  # Restrict proportion MT expression to remove unhappy cells
  seu[[i]] = subset(x = seu[[i]], subset = prop.mt > 0.03 & prop.mt < 0.1)
  
  # Remove potential doublets
  seu[[i]] = subset(x = seu[[i]], subset = nFeature_RNA < 6500 & nCount_RNA < 55000)
  
  seu[[i]] = SCTransform(seu[[i]], verbose = FALSE, vars.to.regress=c("prop.mt", "nFeature_RNA", "nCount_RNA"))
  seu[[i]] = CellCycleScoring(seu[[i]], s.features = cc.genes$s.genes, g2m.features = cc.genes$g2m.genes, assay = 'SCT', set.ident = TRUE)
  # Enable this when also adjusting for cell cycle
  seu[[i]] = SCTransform(seu[[i]], verbose = FALSE, vars.to.regress=c("prop.mt", "nFeature_RNA", "nCount_RNA", "S.Score", "G2M.Score"))
  
  seu[[i]] = AddMetaData(object=seu[[i]], metadata=factor(rep(library_names[i], length(seu[[i]]$nCount_RNA)), levels=library_names), col.name="library")
}

Combine the libraries with application of batch effect correction

# This is required for PrepSCTIntegration, an increase from the R default 512 to 5120 for this dataset
options(future.globals.maxSize=5120*1024^2)

seu_features = SelectIntegrationFeatures(object.list = seu, nfeatures = 3000)
seu = PrepSCTIntegration(object.list = seu, anchor.features = seu_features, verbose = FALSE)
seu = lapply(X = seu, FUN = RunPCA, verbose = FALSE, features = seu_features)
seu_anchors = FindIntegrationAnchors(object.list = seu, normalization.method = "SCT", anchor.features = seu_features, verbose = FALSE, reduction = "rpca")
seu_integrated = IntegrateData(anchorset = seu_anchors, normalization.method = "SCT", verbose = FALSE)
seu_integrated@meta.data$library = factor(seu_integrated@meta.data$library, levels=library_names)

Make some summary plots

seu_integrated = RunPCA(seu_integrated, verbose = FALSE)

VlnPlot(seu_integrated, group.by="orig.ident", features = c("nFeature_RNA", "nCount_RNA", "percent.mito"), ncol = 3, pt.size=0.01)
The following requested variables were not found: percent.mito

ElbowPlot(seu_integrated, ndims=50)

Typically you’d pick a point on the above curve where the tail starts to flatten. I’ve included a few more PCs here to make sure we’re capturing a bit more possibly relevant variation to increase separation between cell types. Went for 30 PCs here.

seu_integrated = RunUMAP(seu_integrated, dims = 1:30)
The default method for RunUMAP has changed from calling Python UMAP via reticulate to the R-native UWOT using the cosine metric
To use Python UMAP via reticulate, set umap.method to 'umap-learn' and metric to 'correlation'
This message will be shown once per session12:36:06 UMAP embedding parameters a = 0.9922 b = 1.112
12:36:06 Read 13111 rows and found 30 numeric columns
12:36:06 Using Annoy for neighbor search, n_neighbors = 30
12:36:06 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
12:36:09 Writing NN index file to temp file /var/folders/bz/p63rv1754pv9v6rz33xgmy8sgyrrg3/T//RtmpsxGViF/fileb6712d3677f1
12:36:09 Searching Annoy index using 1 thread, search_k = 3000
12:36:14 Annoy recall = 100%
12:36:16 Commencing smooth kNN distance calibration using 1 thread
12:36:19 Initializing from normalized Laplacian + noise
12:36:20 Commencing optimization for 200 epochs, with 564502 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
12:36:29 Optimization finished
UMAPPlot(seu_integrated)

p = UMAPPlot(seu_integrated,group.by="library")
p = theme_publication_plot(p, "Library")
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "supplfig_umap_overlay_library.pdf"),
                   base_height=4,
                   base_width=6)


FeaturePlot(seu_integrated, features=c("Krt14", "Tgm3", "Krt4", "Lor"))


VlnPlot(seu_integrated, group.by="orig.ident", features = "prop.mt", ncol = 1, pt.size=0.01)


FeaturePlot(seu_integrated, features=c("nFeature_RNA", "nCount_RNA", "prop.mt"))

Apply batch correction - MT filters and adjust for MT, nGenes and cell cycle - keratinocytes selected

Assign a celltype to all cells

First annotate clusters with expression of a number of marker genes and pick a threshold per cluster. That data is then further down used to assign cell type labels

celltypes = do.call(rbind, celltypes_all)
tpm = calc_tpm(seu_integrated@assays[["RNA"]]@counts, ref_file_mm10)

# get tpm for marker genes per cluster
seu_integrated = FindNeighbors(seu_integrated, dims = 1:30)
Computing nearest neighbor graph
Computing SNN
seu_integrated = FindClusters(object = seu_integrated, resolution = 0.6, verbose=FALSE)
markers_all_cells = FindAllMarkers(seu_integrated, only.pos = TRUE, min.pct = 0.1, thresh.use = 0.25, verbose = F)
mdata = seu_integrated@meta.data
mdata$celltype = NA
inboth = intersect(gsub("-1", "", rownames(mdata)), celltypes$cell)
mdata$celltype[match(gsub("-1", "", rownames(mdata)), inboth)] = celltypes$classification[match(inboth, celltypes$cell)]
seu_integrated = AddMetaData(seu_integrated, mdata$celltype, "celltype")

UMAPPlot(seu_integrated, group.by="celltype")


cluster_classification = data.frame(clusterid=sort(unique(mdata$seurat_clusters)))

make_plot = function(mdata, tpm, gene_name) {
  plot_dat = data.frame(cell=rownames(mdata), cluster=mdata$seurat_clusters, tpm=tpm[gene_name,rownames(mdata)])
  plot_dat = plot_dat[is.finite(plot_dat$tpm),]
  plot_dat_clusters  = plot_dat %>% group_by(cluster) %>% summarise(n=n(), mean=mean(tpm), median=median(tpm))
  p2 = ggplot(plot_dat_clusters) + aes(x=cluster, y=median, label=cluster) + geom_text() + theme_cowplot() + ggtitle(gene_name)
  return(list(p=p2, plot_dat_clusters=plot_dat_clusters))
}

make_umap_plot = function(plot_dat, gene_name) {
  plot_dat[, gene_name] = log(plot_dat[, gene_name])
  gene_name_title = paste0(toupper(substr(gene_name, 1, 1)), substr(gene_name, 2, nchar(gene_name)))
  return(ggplot(plot_dat) + aes_string(x="UMAP_1", y="UMAP_2", colour=gene_name) + 
           geom_point(size=0.25) + scale_colour_gradient(low="grey", high="red") +
           theme_cowplot() + ggtitle(gene_name_title))
}

plot_data = as.data.frame(seu_integrated@reductions$umap@cell.embeddings)

# Keratinocytes
p1 = make_plot(mdata, tpm, "Krt14")
`summarise()` ungrouping output (override with `.groups` argument)
Registered S3 method overwritten by 'cli':
  method     from    
  print.boxx spatstat
cluster_classification$krt14 = p1$plot_dat_clusters$median > 50
plot_data$krt14 = tpm["Krt14", rownames(plot_data)]

p2 = make_plot(mdata, tpm, "Tgm3")
`summarise()` ungrouping output (override with `.groups` argument)
cluster_classification$tgm3 = p2$plot_dat_clusters$median > 5
plot_data$tgm3 = tpm["Tgm3", rownames(plot_data)]

p3 = make_plot(mdata, tpm, "Lor")
`summarise()` ungrouping output (override with `.groups` argument)
cluster_classification$lor = p3$plot_dat_clusters$median > 50
plot_data$lor = tpm["Lor", rownames(plot_data)]

p4 = make_plot(mdata, tpm, "Krt4")
`summarise()` ungrouping output (override with `.groups` argument)
# cluster_classification$tgm3 = plot_dat_clusters$median > 10
plot_data$krt4 = tpm["Krt4", rownames(plot_data)]

plot_grid(plotlist=list(p1$p, p2$p, p3$p, p4$p), ncol=2)

p = plot_grid(plotlist=list(theme_publication_plot(make_umap_plot(plot_data, "krt14"), "log(TPM)"),
                        theme_publication_plot(make_umap_plot(plot_data, "tgm3"), "log(TPM)"),
                        theme_publication_plot(make_umap_plot(plot_data, "lor"), "log(TPM)"),
                        theme_publication_plot(make_umap_plot(plot_data, "krt4"), "log(TPM)")), ncol=2, nrow=2)
print(p)

plot_grid(plotlist=list(make_umap_plot(plot_data, "krt14"),
                        make_umap_plot(plot_data, "krt4")), ncol=2, nrow=2)

plot_grid(plotlist=list(make_umap_plot(plot_data, "krt14"),
                        make_umap_plot(plot_data, "tgm3")), ncol=2, nrow=2)

cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "supplfig_markers_keratinocyte.pdf"),
                   base_height=4,
                   base_width=6)



# Fibroblasts and endothelial cells
p1 = make_plot(mdata, tpm, "Col1a1")
`summarise()` ungrouping output (override with `.groups` argument)
cluster_classification$col1a1 = p1$plot_dat_clusters$median > 10
plot_data$col1a1 = tpm["Col1a1", rownames(plot_data)]

p2 = make_plot(mdata, tpm, "Pecam1")
`summarise()` ungrouping output (override with `.groups` argument)
cluster_classification$pecam1 = p2$plot_dat_clusters$median > 2
plot_data$pcam1 = tpm["Pecam1", rownames(plot_data)]

plot_grid(plotlist=list(p1$p, p2$p), ncol=2)

p = plot_grid(plotlist=list(theme_publication_plot(make_umap_plot(plot_data, "col1a1"), "log(TPM)"),
                        theme_publication_plot(make_umap_plot(plot_data, "pcam1"), "log(TPM)")), ncol=2, nrow=2)
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "supplfig_markers_fibroblast_endothelial.pdf"),
                   base_height=4,
                   base_width=6)



# Immune cells

# B-cell marker
p1 = make_plot(mdata, tpm, "Cd83")
`summarise()` ungrouping output (override with `.groups` argument)
cluster_classification$cd83 = p1$plot_dat_clusters$median > 1
plot_data$cd83 = tpm["Cd83", rownames(plot_data)]

p2 = make_plot(mdata, tpm, "Cd84")
`summarise()` ungrouping output (override with `.groups` argument)
cluster_classification$cd84 = p2$plot_dat_clusters$median > 1
plot_data$cd84 = tpm["Cd84", rownames(plot_data)]

p3 = make_plot(mdata, tpm, "Cd86")
`summarise()` ungrouping output (override with `.groups` argument)
cluster_classification$cd86 = p3$plot_dat_clusters$median > 0.4
plot_data$cd86 = tpm["Cd86", rownames(plot_data)]

plot_grid(plotlist=list(p1$p, p2$p, p3$p), ncol=2)

plot_grid(plotlist=list(make_umap_plot(plot_data, "cd83"),
                        make_umap_plot(plot_data, "cd84"),
                        make_umap_plot(plot_data, "cd86")), ncol=2, nrow=2)


# T-cells

# t-cell marker
p1 = make_plot(mdata, tpm, "Trbc2")
`summarise()` ungrouping output (override with `.groups` argument)
cluster_classification$trbc2 = p1$plot_dat_clusters$median > 50
plot_data$trbc2 = tpm["Trbc2", rownames(plot_data)]

# not specific
p2 = make_plot(mdata, tpm, "Cd52")
`summarise()` ungrouping output (override with `.groups` argument)
cluster_classification$cd52 = p2$plot_dat_clusters$median > 50
plot_data$cd52 = tpm["Cd52", rownames(plot_data)]

# t-cell marker
p3 = make_plot(mdata, tpm, "Ptprc")
`summarise()` ungrouping output (override with `.groups` argument)
cluster_classification$ptprc = p3$plot_dat_clusters$median > 50
plot_data$ptprc = tpm["Ptprc", rownames(plot_data)]

plot_grid(plotlist=list(p1$p, p2$p, p3$p), ncol=2)

p = plot_grid(plotlist=list(theme_publication_plot(make_umap_plot(plot_data, "ptprc"), "log(TPM)"),
                        theme_publication_plot(make_umap_plot(plot_data, "cd52"), "log(TPM)")), ncol=2, nrow=2)
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "supplfig_markers_immune.pdf"),
                   base_height=4,
                   base_width=6)

Handy plot with large cluster numbers

plot <- DimPlot(object = seu_integrated, reduction = "umap")
LabelClusters(plot = plot, id = 'ident', size=6)

Now assign cell type labels based on the above markers and make a plot

cluster_classification$celltype = NA
cluster_classification$celltype[cluster_classification$krt14 | cluster_classification$tgm3 | cluster_classification$lor] = "keratinocyte"
cluster_classification$celltype[cluster_classification$col1a1] = "fibroblast"
cluster_classification$celltype[cluster_classification$pecam1] = "endothelial"
cluster_classification$celltype[cluster_classification$cd83 | cluster_classification$cd84 | cluster_classification$cd86 | cluster_classification$cd52 | cluster_classification$trbc2] = "immune"
cluster_classification_table = cluster_classification$celltype
names(cluster_classification_table) = as.character(cluster_classification$clusterid)
seu_integrated = AddMetaData(seu_integrated, cluster_classification_table[as.character(seu_integrated@meta.data$seurat_clusters)], "celltype")
UMAPPlot(seu_integrated, group.by="celltype")


plot_dat = as.data.frame(seu_integrated@reductions[["umap"]]@cell.embeddings)
plot_dat$celltype = seu_integrated@meta.data$celltype
plot_dat$celltype = unlist(lapply(plot_dat$celltype, function(x) { paste(toupper(substr(x, 1, 1)), substr(x, 2, nchar(x)), sep="") }))
plot_dat$celltype_fctr = factor(plot_dat$celltype, levels=c("Keratinocyte", "Fibroblast", "Immune", "Endothelial"))

A number of purple coloured cells appear in the clusters of other cell types, looking at the above UMAP space. Here their labels are set to NA, as there appears a discrepancy between their cluster assignment and visual clustering in the 2D UMAP space. Taking these few out due to the uncertainty, we don’t want to include any cells of other cell types further down.

plot_data = as.data.frame(seu_integrated@reductions$umap@cell.embeddings)
plot_data$celltype = seu_integrated@meta.data$celltype
plot_data = plot_data[plot_data$celltype=="keratinocyte",]
plot_data$selection = plot_data$UMAP_1 > 5 | plot_data$UMAP_2 > 5
plot_data$selection_fctr = factor(plot_data$selection, levels=c(TRUE, FALSE))

mycolours = c("red", "grey")
names(mycolours) = c(TRUE, FALSE)

p = ggplot(plot_data) + aes_string(x="UMAP_1", y="UMAP_2", colour="selection_fctr") + 
           geom_point(size=0.25) + scale_colour_manual(values=mycolours) +
           theme_cowplot()
print(p)

seu_integrated@meta.data$celltype[rownames(seu_integrated@meta.data) %in% rownames(plot_data)[plot_data$selection]] = NA
UMAPPlot(seu_integrated, group.by="Phase")


plot_data = as.data.frame(seu_integrated@reductions$umap@cell.embeddings)
plot_data$celltype = seu_integrated@meta.data$celltype
plot_data$celltype = unlist(lapply(plot_data$celltype, function(x) { paste(toupper(substr(x, 1, 1)), substr(x, 2, nchar(x)), sep="") }))
plot_data$celltype_fctr = factor(plot_data$celltype, levels=c("Keratinocyte", "Fibroblast", "Immune", "Endothelial"))
mycolours = brewer.pal(4, "Dark2")
p = ggplot(plot_data) + 
  aes(x=UMAP_1, y=UMAP_2, colour=celltype_fctr) + 
  geom_point(size=0.2) + 
  scale_colour_manual(values=mycolours, na.value="grey") +
  xlab("UMAP 1") + ylab("UMAP 2")
p = theme_publication_plot(p, "Cell type")
print(p)

cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "figure5d.pdf"),
                   base_height=4,
                   base_width=6)

Plot Notch1 expression per library

notch1_expr = data.frame(expression=tpm["Notch1", rownames(seu_integrated@meta.data)], library=seu_integrated@meta.data$library)
notch1_expr$library = factor(notch1_expr$library, levels=c("WT 1", "WT 2", "HOM KO 1", "HOM KO 2"))
p = ggplot(notch1_expr) + aes(x=library, y=expression, fill=library) + geom_boxplot() + 
  theme_cowplot() + xlab("Library") + ylab("TPM")
p = theme_publication_plot(p, "Library", legend_aes = 1)
print(p)

cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "supplfig_notch1_expression_all_cells.pdf"),
                   base_height=4,
                   base_width=6)

Save

save(file=paste0(fileprefix, "notch1_batch_effect_with_filter_with_adjustment.RData"), seu_integrated, markers_all_cells)
metdata = seu_integrated@meta.data
metdata$cell = rownames(metdata)
write.table(metdata, file=paste0(fileprefix, "notch1_celltypes_and_metadata.txt"), quote=F, sep="\t", row.names=F)

Cell type counts per library

celltype_count = seu_integrated@meta.data[, c("orig.ident", "celltype")] %>% group_by(orig.ident, celltype) %>% summarise(n=n()) %>% spread(celltype, n)
`summarise()` regrouping output by 'orig.ident' (override with `.groups` argument)
celltype_frac = celltype_count[, 2:ncol(celltype_count)] / rowSums(celltype_count[, 2:ncol(celltype_count)], na.rm=T)
celltype_frac = cbind(data.frame(library=c("WT 1", "WT 2", "HOM KO 1", "HOM KO 2")), celltype_frac)
celltype_frac
write.table(celltype_frac, file=paste0(fileprefix, "notch1_cell_type_counts_per_library.txt"), quote=F, sep="\t", row.names=F)


celltype_frac$library = factor(celltype_frac$library, levels=c("WT 1", "WT 2", "HOM KO 1", "HOM KO 2"))
cell_type_frac_wider = celltype_frac %>% pivot_longer(!library)
cell_type_frac_wider$name = unlist(lapply(cell_type_frac_wider$name, function(x) { paste(toupper(substr(x, 1, 1)), substr(x, 2, nchar(x)), sep="") }))
cell_type_frac_wider$name = factor(cell_type_frac_wider$name, levels=levels(plot_dat$celltype_fctr))
p = ggplot(cell_type_frac_wider) + 
  aes(x=library, y=value, fill=name) + 
  geom_bar(position="dodge", stat="identity") + 
  scale_fill_manual(values=mycolours, na.value="grey") +
  xlab("Library") + ylab("Proportion of cells per library")
p = theme_publication_plot(p, "Cell type")
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "figure5e_alt1.pdf"),
                   base_height=4,
                   base_width=6)


p = ggplot(cell_type_frac_wider) + 
  aes(x=library, y=value, fill=name) + 
  geom_bar(stat="identity", position = position_fill(reverse = TRUE)) + 
  scale_fill_manual(values=mycolours, na.value="grey") +
  xlab("Library") + ylab("Proportion of cells per library")
p = theme_publication_plot(p, "Cell type")
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "figure5e_alt2.pdf"),
                   base_height=4,
                   base_width=6)

Analysis keratinocytes

Rerun integration because we are no longer now including cell cycle adjustment

keratinocyte_barcodes = rownames(seu_integrated@meta.data)[seu_integrated@meta.data$celltype=="keratinocyte" & !is.na(seu_integrated@meta.data$celltype)]

# Initial run found these cells as an outlier cluster and expressing fibroblast markers. Here these are also removed
misidentified_fibroblasts = read.table("20201211_fibroblast_identified_as_keratinocyte.txt", header=T, stringsAsFactors=F)
keratinocyte_barcodes = keratinocyte_barcodes[keratinocyte_barcodes %in% misidentified_fibroblasts$cell[!misidentified_fibroblasts$fibroblast_identified_as_keratinocyte]]

outlier_cells = read.table("20201211_notch1_cluster_assignments_to_remove_outlier_cells.txt", header=T, stringsAsFactors=F)
outlier_cells = outlier_cells[outlier_cells$is_outlier,]
keratinocyte_barcodes = keratinocyte_barcodes[!keratinocyte_barcodes %in% outlier_cells$cell]

seu_kera = list()
for (i in 1:length(expr_matrices)) {
  seu_kera[[i]] = CreateSeuratObject(counts = expr_matrices[[i]][, colnames(expr_matrices[[i]]) %in% keratinocyte_barcodes], min.cells = 30, min.features = 3000)
  
  mito.genes <- rownames(GetAssayData(object=seu_kera[[i]]))[grepl(pattern = "^MT-", x = toupper(rownames(GetAssayData(object=seu_kera[[i]]))))]
  percent.mito <- Matrix::colSums(GetAssayData(object=seu_kera[[i]], slot="counts")[mito.genes, ]) / Matrix::colSums(GetAssayData(object=seu_kera[[i]], slot="counts"))
  seu_kera[[i]] <- AddMetaData(object = seu_kera[[i]], metadata = percent.mito, col.name = "prop.mt")
  
  # Restrict proportion MT expression to remove unhappy cells
  seu_kera[[i]] = subset(x = seu_kera[[i]], subset = prop.mt > 0.03 & prop.mt < 0.1)
  
  # Remove potential doublets
  seu[[i]] = subset(x = seu[[i]], subset = nFeature_RNA < 6500 & nCount_RNA < 55000)
  
  seu_kera[[i]] = SCTransform(seu_kera[[i]], verbose = FALSE, vars.to.regress=c("prop.mt", "nFeature_RNA", "nCount_RNA"))
  seu_kera[[i]] = CellCycleScoring(seu_kera[[i]], s.features = cc.genes$s.genes, g2m.features = cc.genes$g2m.genes, assay = 'SCT', set.ident = TRUE)
  # Enable this when also adjusting for cell cycle
  # seu[[i]] = SCTransform(seu[[i]], verbose = FALSE, vars.to.regress=c("prop.mt", "nFeature_RNA", "nCount_RNA", "S.Score", "G2M.Score"))
  
  seu[[i]] = AddMetaData(object=seu[[i]], metadata=rep(library_names[i], length(seu[[i]]$nCount_RNA)), col.name="library")
}

Rerun the integration with the newly adjusted values

# This is required for PrepSCTIntegration, an increase from the R default 512 to 5120 for this dataset
options(future.globals.maxSize=5120*1024^2)

seu_features = SelectIntegrationFeatures(object.list = seu_kera, nfeatures = 3000)
seu_kera = PrepSCTIntegration(object.list = seu_kera, anchor.features = seu_features, verbose = FALSE)
seu_kera = lapply(X = seu_kera, FUN = RunPCA, verbose = FALSE, features = seu_features)
seu_anchors = FindIntegrationAnchors(object.list = seu_kera, normalization.method = "SCT", anchor.features = seu_features, verbose = FALSE, reduction = "rpca")
seu_kera_integrated = IntegrateData(anchorset = seu_anchors, normalization.method = "SCT", verbose = FALSE)
library_per_cell = factor(unlist(lapply(seu, function(x) x@meta.data$library)), levels=library_names)
names(library_per_cell) = unlist(lapply(seu, function(x) rownames(x@meta.data)))
seu_kera_integrated = AddMetaData(object=seu_kera_integrated, metadata=library_per_cell, col.name="library")
seu_kera_integrated = RunPCA(seu_kera_integrated, verbose = FALSE)

VlnPlot(seu_kera_integrated, group.by="orig.ident", features = c("nFeature_RNA", "nCount_RNA", "prop.mt"), ncol = 3, pt.size=0.01)

ElbowPlot(seu_kera_integrated, ndims=50)

Here we do take the point where the additional variation tail in the above plot becomes flat. Picked 10 PCs.

seu_kera_integrated = RunUMAP(seu_kera_integrated, dims = 1:10)
13:01:04 UMAP embedding parameters a = 0.9922 b = 1.112
13:01:04 Read 8807 rows and found 10 numeric columns
13:01:04 Using Annoy for neighbor search, n_neighbors = 30
13:01:04 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
13:01:07 Writing NN index file to temp file /var/folders/bz/p63rv1754pv9v6rz33xgmy8sgyrrg3/T//RtmpsxGViF/fileb6717dd80e2
13:01:07 Searching Annoy index using 1 thread, search_k = 3000
13:01:09 Annoy recall = 100%
13:01:11 Commencing smooth kNN distance calibration using 1 thread
13:01:14 Initializing from normalized Laplacian + noise
13:01:14 Commencing optimization for 500 epochs, with 362192 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
13:01:27 Optimization finished
UMAPPlot(seu_kera_integrated)

p = UMAPPlot(seu_kera_integrated,group.by="library")
p = theme_publication_plot(p, "Library")
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "supplfig_umap_keratinocytes_overlay_library.pdf"),
                   base_height=4,
                   base_width=6)



UMAPPlot(seu_kera_integrated,group.by="Phase")


plot_dat = as.data.frame(seu_kera_integrated@reductions[["umap"]]@cell.embeddings)
plot_dat$krt14 = log(tpm["Krt14", rownames(seu_kera_integrated@meta.data)])
plot_dat$tgm3 = log(tpm["Tgm3", rownames(seu_kera_integrated@meta.data)])
plot_dat$krt4 = log(tpm["Krt4", rownames(seu_kera_integrated@meta.data)])
plot_dat$lor = log(tpm["Lor", rownames(seu_kera_integrated@meta.data)])

p1 = theme_publication_plot(ggplot(plot_dat) + aes(x=UMAP_1, y=UMAP_2, colour=krt14) + geom_point(size=0.2) + xlab("UMAP 1") + ylab("UMAP 2") + theme_cowplot() + ggtitle("Krt14"), legend_title = "log(TPM)")
p2 = theme_publication_plot(ggplot(plot_dat) + aes(x=UMAP_1, y=UMAP_2, colour=tgm3) + geom_point(size=0.2) + xlab("UMAP 1") + ylab("UMAP 2") + theme_cowplot() + ggtitle("Tgm3"), legend_title = "log(TPM)")
p3 = theme_publication_plot(ggplot(plot_dat) + aes(x=UMAP_1, y=UMAP_2, colour=krt4) + geom_point(size=0.2) + xlab("UMAP 1") + ylab("UMAP 2") + theme_cowplot() + ggtitle("Krt4"), legend_title = "log(TPM)")
p4 = theme_publication_plot(ggplot(plot_dat) + aes(x=UMAP_1, y=UMAP_2, colour=lor) + geom_point(size=0.2) + xlab("UMAP 1") + ylab("UMAP 2") + theme_cowplot() + ggtitle("Lor"), legend_title = "log(TPM)")
p = plot_grid(p1, p2, p3, p4, ncol=2)
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "supplfig_keratinocytes_marker_expression.pdf"),
                   base_height=4,
                   base_width=6)


VlnPlot(seu_kera_integrated, group.by="orig.ident", features = "prop.mt", ncol = 1, pt.size=0.01)


FeaturePlot(seu_kera_integrated, features=c("nFeature_RNA", "nCount_RNA", "prop.mt"))


plot_dat = as.data.frame(seu_kera_integrated@reductions$umap@cell.embeddings)
plot_dat$phase = seu_kera_integrated@meta.data$Phase
plot_dat$phase = factor(plot_dat$phase, levels=c("G1", "G2M", "S"))
# mycolours = brewer.pal(4, "Dark2")
p = ggplot(plot_dat) + 
  aes(x=UMAP_1, y=UMAP_2, colour=phase) + 
  geom_point(size=0.2) + 
  xlab("UMAP 1") + ylab("UMAP 2")
p = theme_publication_plot(p, "Cell type")
print(p)

cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "figure5h.pdf"),
                   base_height=4,
                   base_width=6)


phase_count = seu_kera_integrated@meta.data %>% group_by(library, Phase) %>% summarise(n=n()) %>% mutate(frac = n/sum(n))
`summarise()` regrouping output by 'library' (override with `.groups` argument)
phase_count$library = factor(phase_count$library, levels=c("WT 1", "WT 2", "HOM KO 1", "HOM KO 2"))
p = ggplot(phase_count) + 
  aes(x=library, y=frac, fill=Phase) + 
  geom_bar(position="dodge", stat="identity") + 
  xlab("Library") + ylab("Proportion of cells per library")
p = theme_publication_plot(p, "Cycle phase")
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "figure5i_alt1.pdf"),
                   base_height=4,
                   base_width=6)


p = ggplot(phase_count) + 
  aes(x=library, y=frac, fill=Phase) + 
  geom_bar(stat="identity", position = position_fill(reverse = TRUE)) + 
  xlab("Library") + ylab("Proportion of cells per library")
p = theme_publication_plot(p, "Cycle phase")
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "figure5i_alt2.pdf"),
                   base_height=4,
                   base_width=6)


write.table(phase_count[, c("library", "Phase", "frac")] %>% pivot_wider(names_from=Phase, values_from=frac), 
            file=paste0(fileprefix, "frac_cells_library_cellcycle.txt"), quote=F, sep="\t", row.names=F)
# get tpm for marker genes per cluster
seu_kera_integrated = FindNeighbors(seu_kera_integrated, dims = 1:10)
Computing nearest neighbor graph
Computing SNN
seu_kera_integrated = FindClusters(object = seu_kera_integrated, resolution = 0.6, verbose=FALSE)
markers_keratinocytes = FindAllMarkers(seu_kera_integrated, only.pos = TRUE, min.pct = 0.1, thresh.use = 0.25, verbose = F)
plot <- DimPlot(object = seu_kera_integrated, reduction = "umap")
LabelClusters(plot = plot, id = 'ident', size=6)

write.table(markers_keratinocytes, file=paste0(fileprefix, "signif_expr_markers_keratinocyte_analysis.txt"), quote=F, sep="\t", row.names=F)

Determine number of basal cells

The ‘circle’ is formed of basal cells, the tail of suprabasal. Here we make the observation that known marker genes broadly define the cutoff point, which we approximate with the right below defined slope/intercept of a line. What we really want to know is whether libraries are equally represented on both sides of the line. We’re less concerned with whether this is the optimal separation of basal and suprabasal cells, especially given it seems likely differentiated cells are less likely to make it through the scRNAseq protocols due to their characteristics.

# established to broadly capture the changeover point where Krt14 drops of and Tgm3 starts going up
slope = 1.5
intercept = 5.5

add_threshold = function(p, slope, intercept) {
  return(p + geom_abline(slope=slope, intercept=intercept, colour="black", linetype=2))
}

pc1 = as.data.frame(seu_kera_integrated@reductions[["umap"]]@cell.embeddings[, c(1, 2), drop=F])
pc1$library = as.character(seu_kera_integrated@meta.data$library[match(rownames(pc1), rownames(seu_kera_integrated@meta.data))])
pc1$selected = ifelse(pc1$UMAP_2 > (intercept+slope*pc1$UMAP_1), 0, 1)
pc1$selected = factor(pc1$selected, levels=c(1, 0))
pc1$layer = "Basal"
pc1$layer[pc1$selected=="0"] = "Suprabasal"
pc1$layer = factor(pc1$layer, levels=c("Basal", "Suprabasal"))

Plot the expression of a number of markers relative to the selected threshold to show we’ve broadly captured the crossover area

plot_dat = as.data.frame(seu_kera_integrated@reductions[["umap"]]@cell.embeddings)
plot_dat$krt14 = log(tpm["Krt14", rownames(seu_kera_integrated@meta.data)])
plot_dat$tgm3 = log(tpm["Tgm3", rownames(seu_kera_integrated@meta.data)])
plot_dat$krt4 = log(tpm["Krt4", rownames(seu_kera_integrated@meta.data)])
plot_dat$lor = log(tpm["Lor", rownames(seu_kera_integrated@meta.data)])

p1 = theme_publication_plot(ggplot(plot_dat) + aes(x=UMAP_1, y=UMAP_2, colour=krt14) + geom_point(size=0.2) + xlab("UMAP 1") + ylab("UMAP 2") + theme_cowplot() + ggtitle("Krt14"), legend_title = "log(TPM)")
p2 = theme_publication_plot(ggplot(plot_dat) + aes(x=UMAP_1, y=UMAP_2, colour=tgm3) + geom_point(size=0.2) + xlab("UMAP 1") + ylab("UMAP 2") + theme_cowplot() + ggtitle("Tgm3"), legend_title = "log(TPM)")
p3 = theme_publication_plot(ggplot(plot_dat) + aes(x=UMAP_1, y=UMAP_2, colour=krt4) + geom_point(size=0.2) + xlab("UMAP 1") + ylab("UMAP 2") + theme_cowplot() + ggtitle("Krt4"), legend_title = "log(TPM)")
p4 = theme_publication_plot(ggplot(plot_dat) + aes(x=UMAP_1, y=UMAP_2, colour=lor) + geom_point(size=0.2) + xlab("UMAP 1") + ylab("UMAP 2") + theme_cowplot() + ggtitle("Lor"), legend_title = "log(TPM)")
p = plot_grid(add_threshold(p1, slope, intercept), 
              add_threshold(p2, slope, intercept), 
              add_threshold(p3, slope, intercept), 
              add_threshold(p4, slope, intercept), ncol=2)
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "supplfig_basal_cell_threshold_vs_markers.pdf"),
                   base_height=4,
                   base_width=6)

Plot the threshold per library to show which cells have been marked as basal cells

mycolours = c("red", "grey")
p1 = ggplot(pc1[pc1$library=="WT 1",]) +
  aes(x=UMAP_1, y=UMAP_2, colour=layer) +
  geom_point(size=0.2) +
  scale_colour_manual(values=mycolours, na.value="grey") +
  xlab("UMAP 1") + ylab("UMAP 2") + ggtitle("WT 1") + theme_cowplot() + theme(legend.position="none")

p2 = ggplot(pc1[pc1$library=="WT 2",]) +
  aes(x=UMAP_1, y=UMAP_2, colour=layer) +
  geom_point(size=0.2) +
  scale_colour_manual(values=mycolours, na.value="grey") +
  xlab("UMAP 1") + ylab("UMAP 2") + ggtitle("WT 2") + theme_cowplot() + theme(legend.position="none") 

p3 = ggplot(pc1[pc1$library=="HOM KO 1",]) +
  aes(x=UMAP_1, y=UMAP_2, colour=layer) +
  geom_point(size=0.2) +
  scale_colour_manual(values=mycolours, na.value="grey") +
  xlab("UMAP 1") + ylab("UMAP 2") + ggtitle("HOM KO 1") + theme_cowplot() + theme(legend.position="none")

p4 = ggplot(pc1[pc1$library=="HOM KO 2",]) +
  aes(x=UMAP_1, y=UMAP_2, colour=layer) +
  geom_point(size=0.2) +
  scale_colour_manual(values=mycolours, na.value="grey") +
  xlab("UMAP 1") + ylab("UMAP 2") + ggtitle("HOM KO 2") + theme_cowplot() + theme(legend.position="none")

p = plot_grid(add_threshold(p1, slope, intercept), 
              add_threshold(p2, slope, intercept), 
              add_threshold(p3, slope, intercept), 
              add_threshold(p4, slope, intercept))
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "supplfig_basal_cell_classification_map.pdf"),
                   base_height=4,
                   base_width=6)

Summarise the calls into a table

basal_cell_count = pc1 %>% group_by(selected, library) %>% summarise(n=n()) %>% pivot_wider(names_from=selected, values_from=n)
`summarise()` regrouping output by 'selected' (override with `.groups` argument)
colnames(basal_cell_count)[2:3] = c("num_basal", "num_suprabasal")
basal_cell_count[, c("frac_basal", "frac_suprabasal")] = basal_cell_count[, 2:3] / rowSums(basal_cell_count[, 2:3])
print(basal_cell_count)
write.table(basal_cell_count, file=paste0(fileprefix, "notch1_fraction_basal_cells_umap_threshold.txt"), quote=F, sep="\t", row.names=F)

Make the summary figures for the paper


mycolours = c("red", "grey")
p = ggplot(pc1) + 
  aes(x=UMAP_1, y=UMAP_2, colour=layer) + 
  geom_point(size=0.2) + 
  scale_colour_manual(values=mycolours, na.value="grey") +
  xlab("UMAP 1") + ylab("UMAP 2")
p = theme_publication_plot(p, "Layer")
print(p)

cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "figure5f.pdf"),
                   base_height=4,
                   base_width=6)


basal_cell_count = basal_cell_count[,c("library", "frac_basal", "frac_suprabasal")]
basal_cell_count$library = factor(basal_cell_count$library, levels=c("WT 1", "WT 2", "HOM KO 1", "HOM KO 2"))
colnames(basal_cell_count)[2:3] = c("Basal", "Suprabasal")
p = ggplot(basal_cell_count %>% pivot_longer(!library)) + 
  aes(x=library, y=value, fill=name) + 
  geom_bar(position="dodge", stat="identity") + 
  scale_fill_manual(values=mycolours, na.value="grey") +
  xlab("Library") + ylab("Proportion of cells per library")
p = theme_publication_plot(p, "Layer")
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "figure5g_alt1.pdf"),
                   base_height=4,
                   base_width=6)


p = ggplot(basal_cell_count %>% pivot_longer(!library)) + 
  aes(x=library, y=value, fill=name) +
  geom_bar(stat="identity", position = position_fill(reverse = TRUE)) + 
  scale_fill_manual(values=mycolours, na.value="grey") +
  xlab("Library") + ylab("Proportion of cells per library")
p = theme_publication_plot(p, "Layer")
print(p)
cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "figure5g_alt2.pdf"),
                   base_height=4,
                   base_width=6)

Additional plots

Plot Notch1 expression per library

notch1_expr = data.frame(expression=tpm["Notch1", rownames(seu_kera_integrated@meta.data)], library=seu_kera_integrated@meta.data$library)
notch1_expr$library = factor(notch1_expr$library, levels=c("WT 1", "WT 2", "HOM KO 1", "HOM KO 2"))
p = ggplot(notch1_expr) + aes(x=library, y=expression, fill=library) + geom_boxplot() + 
  theme_cowplot() + xlab("Library") + ylab("TPM")
p = theme_publication_plot(p, "Library", legend_aes=1)
print(p)

cowplot::save_plot(plot=p,
                   filename = paste0(fileprefix, "supplfig_notch1_expression_only_keratinocytes.pdf"),
                   base_height=4,
                   base_width=6)

Downsampled plot to show an equal number of cells per library - 1000 cells

counts_per_library = table(seu_integrated@meta.data$library)
set.seed(123)
selected_cells = rownames(seu_integrated@meta.data)[seu_integrated@meta.data$library=="WT 1"][sample(1:counts_per_library["WT 1"], 1000)]

selected_cells = c(selected_cells, rownames(seu_integrated@meta.data)[seu_integrated@meta.data$library=="WT 2"][sample(1:counts_per_library["WT 2"], 1000)])

selected_cells = c(selected_cells, rownames(seu_integrated@meta.data)[seu_integrated@meta.data$library=="HOM KO 1"][sample(1:counts_per_library["HOM KO 1"], 1000)])

selected_cells = c(selected_cells, rownames(seu_integrated@meta.data)[seu_integrated@meta.data$library=="HOM KO 2"][sample(1:counts_per_library["HOM KO 2"], 1000)])



seu_integrated_temp = subset(seu_integrated, cells=selected_cells)

p = UMAPPlot(seu_integrated_temp,group.by="library")
p = theme_publication_plot(p, "Library")
print(p)
cowplot::save_plot(plot=p,
                   filename = "supplfig_umap_overlay_library_downsampled1000.pdf",
                   base_height=4,
                   base_width=6)


# plot_data = as.data.frame(seu_integrated_temp@reductions[["umap"]]@cell.embeddings)
# plot_data$celltype = seu_integrated_temp@meta.data$celltype
# plot_data$celltype = unlist(lapply(plot_data$celltype, function(x) { paste(toupper(substr(x, 1, 1)), substr(x, 2, nchar(x)), sep="") }))
# plot_data$celltype_fctr = factor(plot_data$celltype, levels=c("Keratinocyte", "Fibroblast", "Immune", "Endothelial"))
# mycolours = brewer.pal(4, "Dark2")
# p = ggplot(plot_data) + 
#   aes(x=UMAP_1, y=UMAP_2, colour=celltype_fctr) + 
#   geom_point(size=0.2) + 
#   scale_colour_manual(values=mycolours, na.value="grey") +
#   xlab("UMAP 1") + ylab("UMAP 2")
# p = theme_publication_plot(p, "Cell type")
# print(p)
# 
# cowplot::save_plot(plot=p,
#                    filename = "figure5d_downsampled1000.pdf",
#                    base_height=4,
#                    base_width=6)

Downsampled plot to show an equal number of cells per library - 1500 cells

counts_per_library = table(seu_integrated@meta.data$library)
set.seed(123)
selected_cells = rownames(seu_integrated@meta.data)[seu_integrated@meta.data$library=="WT 1"][sample(1:counts_per_library["WT 1"], 1500)]

selected_cells = c(selected_cells, rownames(seu_integrated@meta.data)[seu_integrated@meta.data$library=="WT 2"][sample(1:counts_per_library["WT 2"], 1500)])

selected_cells = c(selected_cells, rownames(seu_integrated@meta.data)[seu_integrated@meta.data$library=="HOM KO 1"][sample(1:counts_per_library["HOM KO 1"], 1500)])

selected_cells = c(selected_cells, rownames(seu_integrated@meta.data)[seu_integrated@meta.data$library=="HOM KO 2"][sample(1:counts_per_library["HOM KO 2"], 1500)])



seu_integrated_temp = subset(seu_integrated, cells=selected_cells)

p = UMAPPlot(seu_integrated_temp,group.by="library")
p = theme_publication_plot(p, "Library")
print(p)
cowplot::save_plot(plot=p,
                   filename = "supplfig_umap_overlay_library_downsampled1500.pdf",
                   base_height=4,
                   base_width=6)


# plot_data = as.data.frame(seu_integrated_temp@reductions[["umap"]]@cell.embeddings)
# plot_data$celltype = seu_integrated_temp@meta.data$celltype
# plot_data$celltype = unlist(lapply(plot_data$celltype, function(x) { paste(toupper(substr(x, 1, 1)), substr(x, 2, nchar(x)), sep="") }))
# plot_data$celltype_fctr = factor(plot_data$celltype, levels=c("Keratinocyte", "Fibroblast", "Immune", "Endothelial"))
# mycolours = brewer.pal(4, "Dark2")
# p = ggplot(plot_data) + 
#   aes(x=UMAP_1, y=UMAP_2, colour=celltype_fctr) + 
#   geom_point(size=0.2) + 
#   scale_colour_manual(values=mycolours, na.value="grey") +
#   xlab("UMAP 1") + ylab("UMAP 2")
# p = theme_publication_plot(p, "Cell type")
# print(p)
# 
# cowplot::save_plot(plot=p,
#                    filename = "figure5d_downsampled1500.pdf",
#                    base_height=4,
#                    base_width=6)

Save the outcome

# save the Krt4 estimates
metadat = seu_kera_integrated@meta.data
metadat$cell = rownames(metadat)

metadat = metadat[, c("cell", "orig.ident", "Phase", "prop.mt", "nCount_RNA", "nFeature_RNA")]
colnames(metadat)[2] = "library"

metadat$is_basal = pc1$layer=="Basal"
write.table(metadat, file=paste0(fileprefix, "notch1_basal_suprabasal_calls_umap_threshold.txt"), quote=F, sep="\t", row.names=F)
sessionInfo()
R version 4.0.4 (2021-02-15)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

attached base packages:
[1] parallel  stats4    stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] SoupX_1.4.8                 tidyr_1.1.1                 dplyr_1.0.0                 cowplot_1.0.0               scater_1.16.2              
 [6] ggplot2_3.3.2               SingleCellExperiment_1.10.1 SummarizedExperiment_1.18.2 DelayedArray_0.14.1         matrixStats_0.56.0         
[11] Biobase_2.48.0              GenomicRanges_1.40.0        GenomeInfoDb_1.24.2         IRanges_2.22.2              S4Vectors_0.26.1           
[16] BiocGenerics_0.34.0         Seurat_3.2.0                RColorBrewer_1.1-2          extrafont_0.17             

loaded via a namespace (and not attached):
  [1] ggbeeswarm_0.6.0          Rtsne_0.15                colorspace_1.4-1          deldir_0.1-28             ellipsis_0.3.1           
  [6] ggridges_0.5.2            XVector_0.28.0            BiocNeighbors_1.6.0       base64enc_0.1-3           rstudioapi_0.11          
 [11] spatstat.data_1.4-3       leiden_0.3.3              listenv_0.8.0             ggrepel_0.8.2             codetools_0.2-18         
 [16] splines_4.0.4             knitr_1.29                polyclip_1.10-0           jsonlite_1.7.0            Rttf2pt1_1.3.8           
 [21] ica_1.0-2                 cluster_2.1.0             png_0.1-7                 uwot_0.1.8                shiny_1.5.0              
 [26] sctransform_0.2.1         compiler_4.0.4            httr_1.4.2                Matrix_1.3-2              fastmap_1.0.1            
 [31] lazyeval_0.2.2            BiocSingular_1.4.0        later_1.1.0.1             htmltools_0.5.1.1         tools_4.0.4              
 [36] rsvd_1.0.3                igraph_1.2.6              gtable_0.3.0              glue_1.4.1                GenomeInfoDbData_1.2.3   
 [41] RANN_2.6.1                reshape2_1.4.4            rappdirs_0.3.1            Rcpp_1.0.5                spatstat_1.64-1          
 [46] vctrs_0.3.2               ape_5.4                   nlme_3.1-152              extrafontdb_1.0           DelayedMatrixStats_1.10.1
 [51] lmtest_0.9-37             xfun_0.16                 stringr_1.4.0             globals_0.14.0            mime_0.9                 
 [56] miniUI_0.1.1.1            lifecycle_0.2.0           irlba_2.3.3               goftest_1.2-2             future_1.21.0            
 [61] zlibbioc_1.34.0           MASS_7.3-53               zoo_1.8-8                 scales_1.1.1              promises_1.1.1           
 [66] spatstat.utils_1.17-0     yaml_2.2.1                reticulate_1.16           pbapply_1.4-2             gridExtra_2.3            
 [71] rpart_4.1-15              stringi_1.4.6             BiocParallel_1.22.0       rlang_0.4.7               pkgconfig_2.0.3          
 [76] bitops_1.0-6              evaluate_0.14             lattice_0.20-41           ROCR_1.0-11               purrr_0.3.4              
 [81] tensor_1.5                patchwork_1.0.1           htmlwidgets_1.5.1         tidyselect_1.1.0          parallelly_1.23.0        
 [86] RcppAnnoy_0.0.16          plyr_1.8.6                magrittr_2.0.1            R6_2.4.1                  generics_0.0.2           
 [91] withr_2.2.0               pillar_1.4.6              mgcv_1.8-33               fitdistrplus_1.1-1        survival_3.2-7           
 [96] abind_1.4-5               RCurl_1.98-1.2            tibble_3.0.3              future.apply_1.6.0        crayon_1.3.4             
[101] KernSmooth_2.23-18        plotly_4.9.2.1            rmarkdown_2.3             viridis_0.5.1             grid_4.0.4               
[106] data.table_1.13.0         digest_0.6.25             xtable_1.8-4              httpuv_1.5.4              munsell_0.5.0            
[111] beeswarm_0.2.3            viridisLite_0.3.0         vipor_0.4.5              
LS0tCnRpdGxlOiAiTm90Y2gxIC0gYW5hbHlzaXMgdjEuMSIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IHVuaXRlZAogICAgdG9jOiB5ZXMKLS0tCgojIEZ1bmN0aW9ucyBhbmQgaW1wb3J0cwpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2xhc3Muc291cmNlID0gJ2ZvbGQtaGlkZSd9CmZpbGVwcmVmaXggPSAiMjAyMDEyMjFfIgoKcmVmX2ZpbGVfbW0xMCA9ICJ+L0RvY3VtZW50cy9yZWZlcmVuY2VfZGF0YS9tb3VzZS9lbnNlbWJsL2Vuc2VtYmxfZ2VuZV9sZW5ndGhfbW0xMC50eHQuZ3oiCgojIFRoaXMgaXMgcmVxdWlyZWQgdG8gc2V0IHRoZSBmb250cyBvZiB0aGUgcGFwZXIgcGxvdHMgdG8gQXJpYWwKbGlicmFyeShleHRyYWZvbnQpCmZvbnRfaW1wb3J0KHByb21wdD1GQUxTRSkKbG9hZGZvbnRzKCkKCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShzY2F0ZXIpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShTb3VwWCkKCmNhbGNfdHBtID0gZnVuY3Rpb24oZGF0LCByZWZfZmlsZSkgewogIG1tX2Fubm90ID0gcmVhZC50YWJsZShyZWZfZmlsZSwgaGVhZGVyPVQsIHN0cmluZ3NBc0ZhY3RvcnM9Riwgc2VwPSJcdCIpCiAgZWZmX2xlbmd0aCA9IG1tX2Fubm90JGVmZl9sZW5ndGhbbWF0Y2gocm93bmFtZXMoZGF0KSwgbW1fYW5ub3QkbWdpX3N5bWJvbCldCiAgZWZmX2xlbmd0aFshaXMuZmluaXRlKGVmZl9sZW5ndGgpXSA9IDEKICAKICBzY2UgPSBTaW5nbGVDZWxsRXhwZXJpbWVudChhc3NheXM9bGlzdChjb3VudHM9YXMubWF0cml4KGRhdCksIGxvZ2NvdW50cz1sb2cyKGFzLm1hdHJpeChkYXQpKzEpKSkKICB0cG0gPSBjYWxjdWxhdGVUUE0oc2NlLCBlZmZfbGVuZ3RoKQogIHJldHVybih0cG0pCn0KCnRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QgPSBmdW5jdGlvbihwLCBsZWdlbmRfdGl0bGUsIGxlZ2VuZF9hZXM9NCkgewogIHAgPSBwICsKICB0aGVtZV9jb3dwbG90KCkgKyAKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZT1sZWdlbmRfdGl0bGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT1sZWdlbmRfYWVzKSksCiAgICAgICAgIGNvbG91ciA9IGd1aWRlX2xlZ2VuZCh0aXRsZT1sZWdlbmRfdGl0bGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT1sZWdlbmRfYWVzKSkpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5PSJBcmlhbCIsIGNvbG91cj0iYmxhY2siLHNpemU9MTAsZmFjZT0icGxhaW4iKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhbWlseT0iQXJpYWwiLCBjb2xvdXI9ImJsYWNrIixzaXplPTEyLGZhY2U9InBsYWluIiksCiAgICAgICAgICAgICAgICAgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhbWlseT0iQXJpYWwiLCBjb2xvdXI9ImJsYWNrIixzaXplPTEyLGZhY2U9InBsYWluIiwgYW5nbGU9OTApLAogICAgICAgICAgICAgICAgICAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYW1pbHk9IkFyaWFsIiwgY29sb3VyPSJibGFjayIsc2l6ZT0xMixmYWNlPSJwbGFpbiIsIGFuZ2xlPTM2MCksCiAgICAgICAgICAgICAgICAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5zcGFjaW5nLnggPSB1bml0KDEsICJtbSIpLAogICAgICAgICAgICAgICAgICAgICAgIHBhbmVsLnNwYWNpbmcueSA9IHVuaXQoMywgIm1tIiksCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHk9IkFyaWFsIiwgY29sb3VyPSJibGFjayIsc2l6ZT0xMixmYWNlPSJwbGFpbiIsaGp1c3QgPSAwLjUpKQogIHJldHVybihwKQp9CmBgYAoKIyBMb2FkIHRoZSBkYXRhCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kc2FtcGxlbmFtZXMgPSBjKCJFQV9XVF8xIiwgIkVBX1dUXzIiLCAiRUFfTk9UQ0gxX0hPTV8xIiwgIkVBX05PVENIMV9IT01fMiIpCmxpYnJhcnlfbmFtZXMgPSBjKCJXVCAxIiwgIldUIDIiLCAiSE9NIEtPIDEiLCAiSE9NIEtPIDIiKQpwcmVmaXhlcyA9IGMoImMxXyIsICJjMl8iLCAiczFfIiwgInMyXyIpCmV4cHJfbWF0cmljZXMgPSBsaXN0KCkKY2VsbHR5cGVzX2FsbCA9IGxpc3QoKQpmb3IgKGkgaW4gMTpsZW5ndGgoc2FtcGxlbmFtZXMpKSB7CiAgcHJlZml4ID0gcHJlZml4ZXNbaV0KICBpbl9wYXRoID0gZmlsZS5wYXRoKCIuLi9jZWxscmFuZ2VyLyIsIHNhbXBsZW5hbWVzW2ldKQogIGNlbGx0eXBlcyA9IHJlYWQudGFibGUoZmlsZS5wYXRoKCIuLi9pZGVudGlmeV9jZWxsdHlwZXMiLCBzYW1wbGVuYW1lc1tpXSwgImNlbGx0eXBlc19wZXJfY2VsbC50eHQiKSwgaGVhZGVyPVQsIHN0cmluZ3NBc0ZhY3RvcnM9RikKICBzYyA9IGxvYWQxMFgoaW5fcGF0aCkKICAKICBvdXQgPSBzYyR0b2MKICBjb2xuYW1lcyhvdXQpID0gcGFzdGUwKHByZWZpeCwgY29sbmFtZXMob3V0KSkKICBjZWxsdHlwZXMkY2VsbCA9IHBhc3RlMChwcmVmaXgsIGNlbGx0eXBlcyRjZWxsKQogIAogIGV4cHJfbWF0cmljZXNbW2xlbmd0aChleHByX21hdHJpY2VzKSsxXV0gPSBvdXQKICBjZWxsdHlwZXNfYWxsW1tsZW5ndGgoY2VsbHR5cGVzX2FsbCkrMV1dID0gY2VsbHR5cGVzCn0KYGBgCgojIFJlYWQgaW4gc29tZSBDZWxsUmFuZ2VyIHN0YXRzCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kc3RhdHMgPSBsaXN0KCkKZm9yIChpIGluIDE6bGVuZ3RoKHNhbXBsZW5hbWVzKSkgewogIHN0YXRzX3NhbXBsZSA9IHJlYWQudGFibGUoZmlsZS5wYXRoKCIuLi9jZWxscmFuZ2VyLyIsIHNhbXBsZW5hbWVzW2ldLCAibWV0cmljc19zdW1tYXJ5LmNzdiIpLCBzZXA9IiwiLCBoZWFkZXI9VCwgc3RyaW5nc0FzRmFjdG9ycz1GKQogIHN0YXRzX3NhbXBsZSRsaWJyYXJ5ID0gc2FtcGxlbmFtZXNbaV0KICBzdGF0c1tbc2FtcGxlbmFtZXNbaV1dXSA9IHN0YXRzX3NhbXBsZQp9CnN0YXRzID0gZG8uY2FsbChyYmluZCwgc3RhdHMpCnByaW50KHN0YXRzKQpgYGAKCgojIEV4cGxvcmUgdG8gaWRlbnRpZnkgZmlsdGVycwpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9Cm5GZWF0dXJlX1JOQV9taW4gPSAyNTAwCm5GZWF0dXJlX1JOQV9tYXggPSA2NTAwCm5Db3VudF9STkFfbWF4ID0gNTUwMDAKcHJvcC5tdF9taW4gPSAwLjAzCnByb3AubXRfbWF4ID0gMC4xCgpzZXUgPSBsaXN0KCkKZm9yIChpIGluIDE6bGVuZ3RoKGV4cHJfbWF0cmljZXMpKSB7CiAgc2V1W1tpXV0gPSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gZXhwcl9tYXRyaWNlc1tbaV1dLCBtaW4uY2VsbHMgPSAwLCBtaW4uZmVhdHVyZXMgPSAwKQogIAogIG1pdG8uZ2VuZXMgPSByb3duYW1lcyhHZXRBc3NheURhdGEob2JqZWN0PXNldVtbaV1dKSlbZ3JlcGwocGF0dGVybiA9ICJeTVQtIiwgeCA9IHRvdXBwZXIocm93bmFtZXMoR2V0QXNzYXlEYXRhKG9iamVjdD1zZXVbW2ldXSkpKSldCiAgcGVyY2VudC5taXRvID0gTWF0cml4Ojpjb2xTdW1zKEdldEFzc2F5RGF0YShvYmplY3Q9c2V1W1tpXV0sIHNsb3Q9ImNvdW50cyIpW21pdG8uZ2VuZXMsIF0pIC8gTWF0cml4Ojpjb2xTdW1zKEdldEFzc2F5RGF0YShvYmplY3Q9c2V1W1tpXV0sIHNsb3Q9ImNvdW50cyIpKQogIHNldVtbaV1dID0gQWRkTWV0YURhdGEob2JqZWN0ID0gc2V1W1tpXV0sIG1ldGFkYXRhID0gcGVyY2VudC5taXRvLCBjb2wubmFtZSA9ICJwcm9wLm10IikKICAKICAjIHBsb3QgcHJvcC5tdCB2cyBuRmVhdHVyZV9STkEgYW5kIHZzIG5Db3VudF9STkEKICBwMSA9IGdncGxvdChzZXVbW2ldXUBtZXRhLmRhdGEpICsgYWVzKHg9cHJvcC5tdCwgeT1uRmVhdHVyZV9STkEpICsgZ2VvbV9wb2ludChzaXplPTAuMSwgY29sb3VyPSJyZWQiKSArIHNjYWxlX3lfbG9nMTAoKSArIHRoZW1lX2Nvd3Bsb3QoKSArIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKHByb3AubXRfbWluLCBwcm9wLm10X21heCksIGxpbmV0eXBlPTIpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PWMobkZlYXR1cmVfUk5BX21pbiwgbkZlYXR1cmVfUk5BX21heCksIGxpbmV0eXBlPTIpCiAgCiAgcDIgPSBnZ3Bsb3Qoc2V1W1tpXV1AbWV0YS5kYXRhKSArIGFlcyh4PXByb3AubXQsIHk9bkNvdW50X1JOQSkgKyBnZW9tX3BvaW50KHNpemU9MC4xLCBjb2xvdXI9ImJsdWUiKSArIHNjYWxlX3lfbG9nMTAoKSArIHRoZW1lX2Nvd3Bsb3QoKSArIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKHByb3AubXRfbWluLCBwcm9wLm10X21heCksIGxpbmV0eXBlPTIpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PWMobkNvdW50X1JOQV9tYXgpLCBsaW5ldHlwZT0yKQogIAogIHByaW50KHBsb3RfZ3JpZChwMSwgcDIpKQogIAogIHAxID0gVmxuUGxvdChzZXVbW2ldXSwgZ3JvdXAuYnk9Im9yaWcuaWRlbnQiLCBmZWF0dXJlcyA9IGMoICJuRmVhdHVyZV9STkEiKSwgbmNvbCA9IDEsIHB0LnNpemU9MCkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gYyhuRmVhdHVyZV9STkFfbWluLCBuRmVhdHVyZV9STkFfbWF4KSwgbGluZXR5cGU9MikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQogIHAyID0gVmxuUGxvdChzZXVbW2ldXSwgZ3JvdXAuYnk9Im9yaWcuaWRlbnQiLCBmZWF0dXJlcyA9IGMoICJuQ291bnRfUk5BIiksIG5jb2wgPSAxLCBwdC5zaXplPTApICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMobkNvdW50X1JOQV9tYXgpLCBsaW5ldHlwZT0yKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCiAgcDMgPSBWbG5QbG90KHNldVtbaV1dLCBncm91cC5ieT0ib3JpZy5pZGVudCIsIGZlYXR1cmVzID0gYyggInByb3AubXQiKSwgbmNvbCA9IDEsIHB0LnNpemU9MCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMocHJvcC5tdF9tYXgsIHByb3AubXRfbWluKSwgbGluZXR5cGU9MikKICAKICAjIGlmIChncmVwbCgiS08iLCBzYW1wbGVuYW1lc1tpXSwgZml4ZWQ9VCkpIHsKICAjICAgcDMgPSBwMyArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMoMC4xLCAwLjAwMiksIGxpbmV0eXBlPTIpCiAgIyB9IGVsc2UgewogICMgICBwMyA9IHAzICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gYygwLjEsIDAuMDEpLCBsaW5ldHlwZT0yKQogICMgfQogIAogIHByaW50KHBsb3RfZ3JpZChwMSwgcDIsIHAzLCBuY29sPTMpKQp9CmBgYAoKIyBBbGwgY2VsbHMKIyMgQXBwbHkgYmF0Y2ggY29ycmVjdGlvbiAtIE1UIGZpbHRlcnMgYW5kIGFkanVzdCBmb3IgTVQsIG5HZW5lcyBhbmQgY2VsbCBjeWNsZQoKRmlyc3QgYWRqdXN0bWVudHMgcGVyIGxpYnJhcnkKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpzZXUgPSBsaXN0KCkKZm9yIChpIGluIDE6bGVuZ3RoKGV4cHJfbWF0cmljZXMpKSB7CiAgc2V1W1tpXV0gPSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gZXhwcl9tYXRyaWNlc1tbaV1dLCBtaW4uY2VsbHMgPSAzMCwgbWluLmZlYXR1cmVzID0gMjUwMCkKICAKICBtaXRvLmdlbmVzID0gcm93bmFtZXMoR2V0QXNzYXlEYXRhKG9iamVjdD1zZXVbW2ldXSkpW2dyZXBsKHBhdHRlcm4gPSAiXk1ULSIsIHggPSB0b3VwcGVyKHJvd25hbWVzKEdldEFzc2F5RGF0YShvYmplY3Q9c2V1W1tpXV0pKSkpXQogIHBlcmNlbnQubWl0byA9IE1hdHJpeDo6Y29sU3VtcyhHZXRBc3NheURhdGEob2JqZWN0PXNldVtbaV1dLCBzbG90PSJjb3VudHMiKVttaXRvLmdlbmVzLCBdKSAvIE1hdHJpeDo6Y29sU3VtcyhHZXRBc3NheURhdGEob2JqZWN0PXNldVtbaV1dLCBzbG90PSJjb3VudHMiKSkKICBzZXVbW2ldXSA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IHNldVtbaV1dLCBtZXRhZGF0YSA9IHBlcmNlbnQubWl0bywgY29sLm5hbWUgPSAicHJvcC5tdCIpCiAgCiAgIyBSZXN0cmljdCBwcm9wb3J0aW9uIE1UIGV4cHJlc3Npb24gdG8gcmVtb3ZlIHVuaGFwcHkgY2VsbHMKICBzZXVbW2ldXSA9IHN1YnNldCh4ID0gc2V1W1tpXV0sIHN1YnNldCA9IHByb3AubXQgPiAwLjAzICYgcHJvcC5tdCA8IDAuMSkKICAKICAjIFJlbW92ZSBwb3RlbnRpYWwgZG91YmxldHMKICBzZXVbW2ldXSA9IHN1YnNldCh4ID0gc2V1W1tpXV0sIHN1YnNldCA9IG5GZWF0dXJlX1JOQSA8IDY1MDAgJiBuQ291bnRfUk5BIDwgNTUwMDApCiAgCiAgc2V1W1tpXV0gPSBTQ1RyYW5zZm9ybShzZXVbW2ldXSwgdmVyYm9zZSA9IEZBTFNFLCB2YXJzLnRvLnJlZ3Jlc3M9YygicHJvcC5tdCIsICJuRmVhdHVyZV9STkEiLCAibkNvdW50X1JOQSIpKQogIHNldVtbaV1dID0gQ2VsbEN5Y2xlU2NvcmluZyhzZXVbW2ldXSwgcy5mZWF0dXJlcyA9IGNjLmdlbmVzJHMuZ2VuZXMsIGcybS5mZWF0dXJlcyA9IGNjLmdlbmVzJGcybS5nZW5lcywgYXNzYXkgPSAnU0NUJywgc2V0LmlkZW50ID0gVFJVRSkKICAjIEVuYWJsZSB0aGlzIHdoZW4gYWxzbyBhZGp1c3RpbmcgZm9yIGNlbGwgY3ljbGUKICBzZXVbW2ldXSA9IFNDVHJhbnNmb3JtKHNldVtbaV1dLCB2ZXJib3NlID0gRkFMU0UsIHZhcnMudG8ucmVncmVzcz1jKCJwcm9wLm10IiwgIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiwgIlMuU2NvcmUiLCAiRzJNLlNjb3JlIikpCiAgCiAgc2V1W1tpXV0gPSBBZGRNZXRhRGF0YShvYmplY3Q9c2V1W1tpXV0sIG1ldGFkYXRhPWZhY3RvcihyZXAobGlicmFyeV9uYW1lc1tpXSwgbGVuZ3RoKHNldVtbaV1dJG5Db3VudF9STkEpKSwgbGV2ZWxzPWxpYnJhcnlfbmFtZXMpLCBjb2wubmFtZT0ibGlicmFyeSIpCn0KYGBgCkNvbWJpbmUgdGhlIGxpYnJhcmllcyB3aXRoIGFwcGxpY2F0aW9uIG9mIGJhdGNoIGVmZmVjdCBjb3JyZWN0aW9uCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBUaGlzIGlzIHJlcXVpcmVkIGZvciBQcmVwU0NUSW50ZWdyYXRpb24sIGFuIGluY3JlYXNlIGZyb20gdGhlIFIgZGVmYXVsdCA1MTIgdG8gNTEyMCBmb3IgdGhpcyBkYXRhc2V0Cm9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZT01MTIwKjEwMjReMikKCnNldV9mZWF0dXJlcyA9IFNlbGVjdEludGVncmF0aW9uRmVhdHVyZXMob2JqZWN0Lmxpc3QgPSBzZXUsIG5mZWF0dXJlcyA9IDMwMDApCnNldSA9IFByZXBTQ1RJbnRlZ3JhdGlvbihvYmplY3QubGlzdCA9IHNldSwgYW5jaG9yLmZlYXR1cmVzID0gc2V1X2ZlYXR1cmVzLCB2ZXJib3NlID0gRkFMU0UpCnNldSA9IGxhcHBseShYID0gc2V1LCBGVU4gPSBSdW5QQ0EsIHZlcmJvc2UgPSBGQUxTRSwgZmVhdHVyZXMgPSBzZXVfZmVhdHVyZXMpCnNldV9hbmNob3JzID0gRmluZEludGVncmF0aW9uQW5jaG9ycyhvYmplY3QubGlzdCA9IHNldSwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiU0NUIiwgYW5jaG9yLmZlYXR1cmVzID0gc2V1X2ZlYXR1cmVzLCB2ZXJib3NlID0gRkFMU0UsIHJlZHVjdGlvbiA9ICJycGNhIikKc2V1X2ludGVncmF0ZWQgPSBJbnRlZ3JhdGVEYXRhKGFuY2hvcnNldCA9IHNldV9hbmNob3JzLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJTQ1QiLCB2ZXJib3NlID0gRkFMU0UpCnNldV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSRsaWJyYXJ5ID0gZmFjdG9yKHNldV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSRsaWJyYXJ5LCBsZXZlbHM9bGlicmFyeV9uYW1lcykKYGBgCk1ha2Ugc29tZSBzdW1tYXJ5IHBsb3RzCmBgYHtyfQpzZXVfaW50ZWdyYXRlZCA9IFJ1blBDQShzZXVfaW50ZWdyYXRlZCwgdmVyYm9zZSA9IEZBTFNFKQoKVmxuUGxvdChzZXVfaW50ZWdyYXRlZCwgZ3JvdXAuYnk9Im9yaWcuaWRlbnQiLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiwgInBlcmNlbnQubWl0byIpLCBuY29sID0gMywgcHQuc2l6ZT0wLjAxKQpFbGJvd1Bsb3Qoc2V1X2ludGVncmF0ZWQsIG5kaW1zPTUwKQpgYGAKVHlwaWNhbGx5IHlvdSdkIHBpY2sgYSBwb2ludCBvbiB0aGUgYWJvdmUgY3VydmUgd2hlcmUgdGhlIHRhaWwgc3RhcnRzIHRvIGZsYXR0ZW4uIEkndmUgaW5jbHVkZWQgYSBmZXcgbW9yZSBQQ3MgaGVyZSB0byBtYWtlIHN1cmUgd2UncmUgY2FwdHVyaW5nIGEgYml0IG1vcmUgcG9zc2libHkgcmVsZXZhbnQgdmFyaWF0aW9uIHRvIGluY3JlYXNlIHNlcGFyYXRpb24gYmV0d2VlbiBjZWxsIHR5cGVzLiBXZW50IGZvciAzMCBQQ3MgaGVyZS4KYGBge3J9CnNldV9pbnRlZ3JhdGVkID0gUnVuVU1BUChzZXVfaW50ZWdyYXRlZCwgZGltcyA9IDE6MzApClVNQVBQbG90KHNldV9pbnRlZ3JhdGVkKQpwID0gVU1BUFBsb3Qoc2V1X2ludGVncmF0ZWQsZ3JvdXAuYnk9ImxpYnJhcnkiKQpwID0gdGhlbWVfcHVibGljYXRpb25fcGxvdChwLCAiTGlicmFyeSIpCnByaW50KHApCmNvd3Bsb3Q6OnNhdmVfcGxvdChwbG90PXAsCiAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9IHBhc3RlMChmaWxlcHJlZml4LCAic3VwcGxmaWdfdW1hcF9vdmVybGF5X2xpYnJhcnkucGRmIiksCiAgICAgICAgICAgICAgICAgICBiYXNlX2hlaWdodD00LAogICAgICAgICAgICAgICAgICAgYmFzZV93aWR0aD02KQoKRmVhdHVyZVBsb3Qoc2V1X2ludGVncmF0ZWQsIGZlYXR1cmVzPWMoIktydDE0IiwgIlRnbTMiLCAiS3J0NCIsICJMb3IiKSkKClZsblBsb3Qoc2V1X2ludGVncmF0ZWQsIGdyb3VwLmJ5PSJvcmlnLmlkZW50IiwgZmVhdHVyZXMgPSAicHJvcC5tdCIsIG5jb2wgPSAxLCBwdC5zaXplPTAuMDEpCgpGZWF0dXJlUGxvdChzZXVfaW50ZWdyYXRlZCwgZmVhdHVyZXM9YygibkZlYXR1cmVfUk5BIiwgIm5Db3VudF9STkEiLCAicHJvcC5tdCIpKQpgYGAKCiMjIEFwcGx5IGJhdGNoIGNvcnJlY3Rpb24gLSBNVCBmaWx0ZXJzIGFuZCBhZGp1c3QgZm9yIE1ULCBuR2VuZXMgYW5kIGNlbGwgY3ljbGUgLSBrZXJhdGlub2N5dGVzIHNlbGVjdGVkCgojIyMgQXNzaWduIGEgY2VsbHR5cGUgdG8gYWxsIGNlbGxzCgpGaXJzdCBhbm5vdGF0ZSBjbHVzdGVycyB3aXRoIGV4cHJlc3Npb24gb2YgYSBudW1iZXIgb2YgbWFya2VyIGdlbmVzIGFuZCBwaWNrIGEgdGhyZXNob2xkIHBlciBjbHVzdGVyLiBUaGF0IGRhdGEgaXMgdGhlbiBmdXJ0aGVyIGRvd24gdXNlZCB0byBhc3NpZ24gY2VsbCB0eXBlIGxhYmVscwpgYGB7cn0KY2VsbHR5cGVzID0gZG8uY2FsbChyYmluZCwgY2VsbHR5cGVzX2FsbCkKdHBtID0gY2FsY190cG0oc2V1X2ludGVncmF0ZWRAYXNzYXlzW1siUk5BIl1dQGNvdW50cywgcmVmX2ZpbGVfbW0xMCkKCiMgZ2V0IHRwbSBmb3IgbWFya2VyIGdlbmVzIHBlciBjbHVzdGVyCnNldV9pbnRlZ3JhdGVkID0gRmluZE5laWdoYm9ycyhzZXVfaW50ZWdyYXRlZCwgZGltcyA9IDE6MzApCnNldV9pbnRlZ3JhdGVkID0gRmluZENsdXN0ZXJzKG9iamVjdCA9IHNldV9pbnRlZ3JhdGVkLCByZXNvbHV0aW9uID0gMC42LCB2ZXJib3NlPUZBTFNFKQptYXJrZXJzX2FsbF9jZWxscyA9IEZpbmRBbGxNYXJrZXJzKHNldV9pbnRlZ3JhdGVkLCBvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjEsIHRocmVzaC51c2UgPSAwLjI1LCB2ZXJib3NlID0gRikKCm1kYXRhID0gc2V1X2ludGVncmF0ZWRAbWV0YS5kYXRhCm1kYXRhJGNlbGx0eXBlID0gTkEKaW5ib3RoID0gaW50ZXJzZWN0KGdzdWIoIi0xIiwgIiIsIHJvd25hbWVzKG1kYXRhKSksIGNlbGx0eXBlcyRjZWxsKQptZGF0YSRjZWxsdHlwZVttYXRjaChnc3ViKCItMSIsICIiLCByb3duYW1lcyhtZGF0YSkpLCBpbmJvdGgpXSA9IGNlbGx0eXBlcyRjbGFzc2lmaWNhdGlvblttYXRjaChpbmJvdGgsIGNlbGx0eXBlcyRjZWxsKV0Kc2V1X2ludGVncmF0ZWQgPSBBZGRNZXRhRGF0YShzZXVfaW50ZWdyYXRlZCwgbWRhdGEkY2VsbHR5cGUsICJjZWxsdHlwZSIpCgpVTUFQUGxvdChzZXVfaW50ZWdyYXRlZCwgZ3JvdXAuYnk9ImNlbGx0eXBlIikKCmNsdXN0ZXJfY2xhc3NpZmljYXRpb24gPSBkYXRhLmZyYW1lKGNsdXN0ZXJpZD1zb3J0KHVuaXF1ZShtZGF0YSRzZXVyYXRfY2x1c3RlcnMpKSkKCm1ha2VfcGxvdCA9IGZ1bmN0aW9uKG1kYXRhLCB0cG0sIGdlbmVfbmFtZSkgewogIHBsb3RfZGF0ID0gZGF0YS5mcmFtZShjZWxsPXJvd25hbWVzKG1kYXRhKSwgY2x1c3Rlcj1tZGF0YSRzZXVyYXRfY2x1c3RlcnMsIHRwbT10cG1bZ2VuZV9uYW1lLHJvd25hbWVzKG1kYXRhKV0pCiAgcGxvdF9kYXQgPSBwbG90X2RhdFtpcy5maW5pdGUocGxvdF9kYXQkdHBtKSxdCiAgcGxvdF9kYXRfY2x1c3RlcnMgID0gcGxvdF9kYXQgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSBzdW1tYXJpc2Uobj1uKCksIG1lYW49bWVhbih0cG0pLCBtZWRpYW49bWVkaWFuKHRwbSkpCiAgcDIgPSBnZ3Bsb3QocGxvdF9kYXRfY2x1c3RlcnMpICsgYWVzKHg9Y2x1c3RlciwgeT1tZWRpYW4sIGxhYmVsPWNsdXN0ZXIpICsgZ2VvbV90ZXh0KCkgKyB0aGVtZV9jb3dwbG90KCkgKyBnZ3RpdGxlKGdlbmVfbmFtZSkKICByZXR1cm4obGlzdChwPXAyLCBwbG90X2RhdF9jbHVzdGVycz1wbG90X2RhdF9jbHVzdGVycykpCn0KCm1ha2VfdW1hcF9wbG90ID0gZnVuY3Rpb24ocGxvdF9kYXQsIGdlbmVfbmFtZSkgewogIHBsb3RfZGF0WywgZ2VuZV9uYW1lXSA9IGxvZyhwbG90X2RhdFssIGdlbmVfbmFtZV0pCiAgZ2VuZV9uYW1lX3RpdGxlID0gcGFzdGUwKHRvdXBwZXIoc3Vic3RyKGdlbmVfbmFtZSwgMSwgMSkpLCBzdWJzdHIoZ2VuZV9uYW1lLCAyLCBuY2hhcihnZW5lX25hbWUpKSkKICByZXR1cm4oZ2dwbG90KHBsb3RfZGF0KSArIGFlc19zdHJpbmcoeD0iVU1BUF8xIiwgeT0iVU1BUF8yIiwgY29sb3VyPWdlbmVfbmFtZSkgKyAKICAgICAgICAgICBnZW9tX3BvaW50KHNpemU9MC4yNSkgKyBzY2FsZV9jb2xvdXJfZ3JhZGllbnQobG93PSJncmV5IiwgaGlnaD0icmVkIikgKwogICAgICAgICAgIHRoZW1lX2Nvd3Bsb3QoKSArIGdndGl0bGUoZ2VuZV9uYW1lX3RpdGxlKSkKfQoKcGxvdF9kYXRhID0gYXMuZGF0YS5mcmFtZShzZXVfaW50ZWdyYXRlZEByZWR1Y3Rpb25zJHVtYXBAY2VsbC5lbWJlZGRpbmdzKQoKIyBLZXJhdGlub2N5dGVzCnAxID0gbWFrZV9wbG90KG1kYXRhLCB0cG0sICJLcnQxNCIpCmNsdXN0ZXJfY2xhc3NpZmljYXRpb24ka3J0MTQgPSBwMSRwbG90X2RhdF9jbHVzdGVycyRtZWRpYW4gPiA1MApwbG90X2RhdGEka3J0MTQgPSB0cG1bIktydDE0Iiwgcm93bmFtZXMocGxvdF9kYXRhKV0KCnAyID0gbWFrZV9wbG90KG1kYXRhLCB0cG0sICJUZ20zIikKY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiR0Z20zID0gcDIkcGxvdF9kYXRfY2x1c3RlcnMkbWVkaWFuID4gNQpwbG90X2RhdGEkdGdtMyA9IHRwbVsiVGdtMyIsIHJvd25hbWVzKHBsb3RfZGF0YSldCgpwMyA9IG1ha2VfcGxvdChtZGF0YSwgdHBtLCAiTG9yIikKY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiRsb3IgPSBwMyRwbG90X2RhdF9jbHVzdGVycyRtZWRpYW4gPiA1MApwbG90X2RhdGEkbG9yID0gdHBtWyJMb3IiLCByb3duYW1lcyhwbG90X2RhdGEpXQoKcDQgPSBtYWtlX3Bsb3QobWRhdGEsIHRwbSwgIktydDQiKQojIGNsdXN0ZXJfY2xhc3NpZmljYXRpb24kdGdtMyA9IHBsb3RfZGF0X2NsdXN0ZXJzJG1lZGlhbiA+IDEwCnBsb3RfZGF0YSRrcnQ0ID0gdHBtWyJLcnQ0Iiwgcm93bmFtZXMocGxvdF9kYXRhKV0KCnBsb3RfZ3JpZChwbG90bGlzdD1saXN0KHAxJHAsIHAyJHAsIHAzJHAsIHA0JHApLCBuY29sPTIpCnAgPSBwbG90X2dyaWQocGxvdGxpc3Q9bGlzdCh0aGVtZV9wdWJsaWNhdGlvbl9wbG90KG1ha2VfdW1hcF9wbG90KHBsb3RfZGF0YSwgImtydDE0IiksICJsb2coVFBNKSIpLAogICAgICAgICAgICAgICAgICAgICAgICB0aGVtZV9wdWJsaWNhdGlvbl9wbG90KG1ha2VfdW1hcF9wbG90KHBsb3RfZGF0YSwgInRnbTMiKSwgImxvZyhUUE0pIiksCiAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QobWFrZV91bWFwX3Bsb3QocGxvdF9kYXRhLCAibG9yIiksICJsb2coVFBNKSIpLAogICAgICAgICAgICAgICAgICAgICAgICB0aGVtZV9wdWJsaWNhdGlvbl9wbG90KG1ha2VfdW1hcF9wbG90KHBsb3RfZGF0YSwgImtydDQiKSwgImxvZyhUUE0pIikpLCBuY29sPTIsIG5yb3c9MikKcHJpbnQocCkKcGxvdF9ncmlkKHBsb3RsaXN0PWxpc3QobWFrZV91bWFwX3Bsb3QocGxvdF9kYXRhLCAia3J0MTQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgbWFrZV91bWFwX3Bsb3QocGxvdF9kYXRhLCAia3J0NCIpKSwgbmNvbD0yLCBucm93PTIpCnBsb3RfZ3JpZChwbG90bGlzdD1saXN0KG1ha2VfdW1hcF9wbG90KHBsb3RfZGF0YSwgImtydDE0IiksCiAgICAgICAgICAgICAgICAgICAgICAgIG1ha2VfdW1hcF9wbG90KHBsb3RfZGF0YSwgInRnbTMiKSksIG5jb2w9MiwgbnJvdz0yKQoKY293cGxvdDo6c2F2ZV9wbG90KHBsb3Q9cCwKICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gcGFzdGUwKGZpbGVwcmVmaXgsICJzdXBwbGZpZ19tYXJrZXJzX2tlcmF0aW5vY3l0ZS5wZGYiKSwKICAgICAgICAgICAgICAgICAgIGJhc2VfaGVpZ2h0PTQsCiAgICAgICAgICAgICAgICAgICBiYXNlX3dpZHRoPTYpCgoKIyBGaWJyb2JsYXN0cyBhbmQgZW5kb3RoZWxpYWwgY2VsbHMKcDEgPSBtYWtlX3Bsb3QobWRhdGEsIHRwbSwgIkNvbDFhMSIpCmNsdXN0ZXJfY2xhc3NpZmljYXRpb24kY29sMWExID0gcDEkcGxvdF9kYXRfY2x1c3RlcnMkbWVkaWFuID4gMTAKcGxvdF9kYXRhJGNvbDFhMSA9IHRwbVsiQ29sMWExIiwgcm93bmFtZXMocGxvdF9kYXRhKV0KCnAyID0gbWFrZV9wbG90KG1kYXRhLCB0cG0sICJQZWNhbTEiKQpjbHVzdGVyX2NsYXNzaWZpY2F0aW9uJHBlY2FtMSA9IHAyJHBsb3RfZGF0X2NsdXN0ZXJzJG1lZGlhbiA+IDIKcGxvdF9kYXRhJHBjYW0xID0gdHBtWyJQZWNhbTEiLCByb3duYW1lcyhwbG90X2RhdGEpXQoKcGxvdF9ncmlkKHBsb3RsaXN0PWxpc3QocDEkcCwgcDIkcCksIG5jb2w9MikKcCA9IHBsb3RfZ3JpZChwbG90bGlzdD1saXN0KHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QobWFrZV91bWFwX3Bsb3QocGxvdF9kYXRhLCAiY29sMWExIiksICJsb2coVFBNKSIpLAogICAgICAgICAgICAgICAgICAgICAgICB0aGVtZV9wdWJsaWNhdGlvbl9wbG90KG1ha2VfdW1hcF9wbG90KHBsb3RfZGF0YSwgInBjYW0xIiksICJsb2coVFBNKSIpKSwgbmNvbD0yLCBucm93PTIpCnByaW50KHApCmNvd3Bsb3Q6OnNhdmVfcGxvdChwbG90PXAsCiAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9IHBhc3RlMChmaWxlcHJlZml4LCAic3VwcGxmaWdfbWFya2Vyc19maWJyb2JsYXN0X2VuZG90aGVsaWFsLnBkZiIpLAogICAgICAgICAgICAgICAgICAgYmFzZV9oZWlnaHQ9NCwKICAgICAgICAgICAgICAgICAgIGJhc2Vfd2lkdGg9NikKCgojIEltbXVuZSBjZWxscwoKIyBCLWNlbGwgbWFya2VyCnAxID0gbWFrZV9wbG90KG1kYXRhLCB0cG0sICJDZDgzIikKY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiRjZDgzID0gcDEkcGxvdF9kYXRfY2x1c3RlcnMkbWVkaWFuID4gMQpwbG90X2RhdGEkY2Q4MyA9IHRwbVsiQ2Q4MyIsIHJvd25hbWVzKHBsb3RfZGF0YSldCgpwMiA9IG1ha2VfcGxvdChtZGF0YSwgdHBtLCAiQ2Q4NCIpCmNsdXN0ZXJfY2xhc3NpZmljYXRpb24kY2Q4NCA9IHAyJHBsb3RfZGF0X2NsdXN0ZXJzJG1lZGlhbiA+IDEKcGxvdF9kYXRhJGNkODQgPSB0cG1bIkNkODQiLCByb3duYW1lcyhwbG90X2RhdGEpXQoKcDMgPSBtYWtlX3Bsb3QobWRhdGEsIHRwbSwgIkNkODYiKQpjbHVzdGVyX2NsYXNzaWZpY2F0aW9uJGNkODYgPSBwMyRwbG90X2RhdF9jbHVzdGVycyRtZWRpYW4gPiAwLjQKcGxvdF9kYXRhJGNkODYgPSB0cG1bIkNkODYiLCByb3duYW1lcyhwbG90X2RhdGEpXQoKcGxvdF9ncmlkKHBsb3RsaXN0PWxpc3QocDEkcCwgcDIkcCwgcDMkcCksIG5jb2w9MikKcGxvdF9ncmlkKHBsb3RsaXN0PWxpc3QobWFrZV91bWFwX3Bsb3QocGxvdF9kYXRhLCAiY2Q4MyIpLAogICAgICAgICAgICAgICAgICAgICAgICBtYWtlX3VtYXBfcGxvdChwbG90X2RhdGEsICJjZDg0IiksCiAgICAgICAgICAgICAgICAgICAgICAgIG1ha2VfdW1hcF9wbG90KHBsb3RfZGF0YSwgImNkODYiKSksIG5jb2w9MiwgbnJvdz0yKQoKIyBULWNlbGxzCgojIHQtY2VsbCBtYXJrZXIKcDEgPSBtYWtlX3Bsb3QobWRhdGEsIHRwbSwgIlRyYmMyIikKY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiR0cmJjMiA9IHAxJHBsb3RfZGF0X2NsdXN0ZXJzJG1lZGlhbiA+IDUwCnBsb3RfZGF0YSR0cmJjMiA9IHRwbVsiVHJiYzIiLCByb3duYW1lcyhwbG90X2RhdGEpXQoKIyBub3Qgc3BlY2lmaWMKcDIgPSBtYWtlX3Bsb3QobWRhdGEsIHRwbSwgIkNkNTIiKQpjbHVzdGVyX2NsYXNzaWZpY2F0aW9uJGNkNTIgPSBwMiRwbG90X2RhdF9jbHVzdGVycyRtZWRpYW4gPiA1MApwbG90X2RhdGEkY2Q1MiA9IHRwbVsiQ2Q1MiIsIHJvd25hbWVzKHBsb3RfZGF0YSldCgojIHQtY2VsbCBtYXJrZXIKcDMgPSBtYWtlX3Bsb3QobWRhdGEsIHRwbSwgIlB0cHJjIikKY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiRwdHByYyA9IHAzJHBsb3RfZGF0X2NsdXN0ZXJzJG1lZGlhbiA+IDUwCnBsb3RfZGF0YSRwdHByYyA9IHRwbVsiUHRwcmMiLCByb3duYW1lcyhwbG90X2RhdGEpXQoKcGxvdF9ncmlkKHBsb3RsaXN0PWxpc3QocDEkcCwgcDIkcCwgcDMkcCksIG5jb2w9MikKcCA9IHBsb3RfZ3JpZChwbG90bGlzdD1saXN0KHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QobWFrZV91bWFwX3Bsb3QocGxvdF9kYXRhLCAicHRwcmMiKSwgImxvZyhUUE0pIiksCiAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QobWFrZV91bWFwX3Bsb3QocGxvdF9kYXRhLCAiY2Q1MiIpLCAibG9nKFRQTSkiKSksIG5jb2w9MiwgbnJvdz0yKQpwcmludChwKQpjb3dwbG90OjpzYXZlX3Bsb3QocGxvdD1wLAogICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAoZmlsZXByZWZpeCwgInN1cHBsZmlnX21hcmtlcnNfaW1tdW5lLnBkZiIpLAogICAgICAgICAgICAgICAgICAgYmFzZV9oZWlnaHQ9NCwKICAgICAgICAgICAgICAgICAgIGJhc2Vfd2lkdGg9NikKYGBgCkhhbmR5IHBsb3Qgd2l0aCBsYXJnZSBjbHVzdGVyIG51bWJlcnMKYGBge3J9CnBsb3QgPC0gRGltUGxvdChvYmplY3QgPSBzZXVfaW50ZWdyYXRlZCwgcmVkdWN0aW9uID0gInVtYXAiKQpMYWJlbENsdXN0ZXJzKHBsb3QgPSBwbG90LCBpZCA9ICdpZGVudCcsIHNpemU9NikKYGBgCgpOb3cgYXNzaWduIGNlbGwgdHlwZSBsYWJlbHMgYmFzZWQgb24gdGhlIGFib3ZlIG1hcmtlcnMgYW5kIG1ha2UgYSBwbG90CmBgYHtyfQpjbHVzdGVyX2NsYXNzaWZpY2F0aW9uJGNlbGx0eXBlID0gTkEKY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiRjZWxsdHlwZVtjbHVzdGVyX2NsYXNzaWZpY2F0aW9uJGtydDE0IHwgY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiR0Z20zIHwgY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiRsb3JdID0gImtlcmF0aW5vY3l0ZSIKY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiRjZWxsdHlwZVtjbHVzdGVyX2NsYXNzaWZpY2F0aW9uJGNvbDFhMV0gPSAiZmlicm9ibGFzdCIKY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiRjZWxsdHlwZVtjbHVzdGVyX2NsYXNzaWZpY2F0aW9uJHBlY2FtMV0gPSAiZW5kb3RoZWxpYWwiCmNsdXN0ZXJfY2xhc3NpZmljYXRpb24kY2VsbHR5cGVbY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiRjZDgzIHwgY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiRjZDg0IHwgY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiRjZDg2IHwgY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiRjZDUyIHwgY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiR0cmJjMl0gPSAiaW1tdW5lIgpjbHVzdGVyX2NsYXNzaWZpY2F0aW9uX3RhYmxlID0gY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbiRjZWxsdHlwZQpuYW1lcyhjbHVzdGVyX2NsYXNzaWZpY2F0aW9uX3RhYmxlKSA9IGFzLmNoYXJhY3RlcihjbHVzdGVyX2NsYXNzaWZpY2F0aW9uJGNsdXN0ZXJpZCkKc2V1X2ludGVncmF0ZWQgPSBBZGRNZXRhRGF0YShzZXVfaW50ZWdyYXRlZCwgY2x1c3Rlcl9jbGFzc2lmaWNhdGlvbl90YWJsZVthcy5jaGFyYWN0ZXIoc2V1X2ludGVncmF0ZWRAbWV0YS5kYXRhJHNldXJhdF9jbHVzdGVycyldLCAiY2VsbHR5cGUiKQpVTUFQUGxvdChzZXVfaW50ZWdyYXRlZCwgZ3JvdXAuYnk9ImNlbGx0eXBlIikKCnBsb3RfZGF0ID0gYXMuZGF0YS5mcmFtZShzZXVfaW50ZWdyYXRlZEByZWR1Y3Rpb25zW1sidW1hcCJdXUBjZWxsLmVtYmVkZGluZ3MpCnBsb3RfZGF0JGNlbGx0eXBlID0gc2V1X2ludGVncmF0ZWRAbWV0YS5kYXRhJGNlbGx0eXBlCnBsb3RfZGF0JGNlbGx0eXBlID0gdW5saXN0KGxhcHBseShwbG90X2RhdCRjZWxsdHlwZSwgZnVuY3Rpb24oeCkgeyBwYXN0ZSh0b3VwcGVyKHN1YnN0cih4LCAxLCAxKSksIHN1YnN0cih4LCAyLCBuY2hhcih4KSksIHNlcD0iIikgfSkpCnBsb3RfZGF0JGNlbGx0eXBlX2ZjdHIgPSBmYWN0b3IocGxvdF9kYXQkY2VsbHR5cGUsIGxldmVscz1jKCJLZXJhdGlub2N5dGUiLCAiRmlicm9ibGFzdCIsICJJbW11bmUiLCAiRW5kb3RoZWxpYWwiKSkKYGBgCgpBIG51bWJlciBvZiBwdXJwbGUgY29sb3VyZWQgY2VsbHMgYXBwZWFyIGluIHRoZSBjbHVzdGVycyBvZiBvdGhlciBjZWxsIHR5cGVzLCBsb29raW5nIGF0IHRoZSBhYm92ZSBVTUFQIHNwYWNlLiBIZXJlIHRoZWlyIGxhYmVscyBhcmUgc2V0IHRvIE5BLCBhcyB0aGVyZSBhcHBlYXJzIGEgZGlzY3JlcGFuY3kgYmV0d2VlbiB0aGVpciBjbHVzdGVyIGFzc2lnbm1lbnQgYW5kIHZpc3VhbCBjbHVzdGVyaW5nIGluIHRoZSAyRCBVTUFQIHNwYWNlLiBUYWtpbmcgdGhlc2UgZmV3IG91dCBkdWUgdG8gdGhlIHVuY2VydGFpbnR5LCB3ZSBkb24ndCB3YW50IHRvIGluY2x1ZGUgYW55IGNlbGxzIG9mIG90aGVyIGNlbGwgdHlwZXMgZnVydGhlciBkb3duLgpgYGB7cn0KcGxvdF9kYXRhID0gYXMuZGF0YS5mcmFtZShzZXVfaW50ZWdyYXRlZEByZWR1Y3Rpb25zJHVtYXBAY2VsbC5lbWJlZGRpbmdzKQpwbG90X2RhdGEkY2VsbHR5cGUgPSBzZXVfaW50ZWdyYXRlZEBtZXRhLmRhdGEkY2VsbHR5cGUKcGxvdF9kYXRhID0gcGxvdF9kYXRhW3Bsb3RfZGF0YSRjZWxsdHlwZT09ImtlcmF0aW5vY3l0ZSIsXQpwbG90X2RhdGEkc2VsZWN0aW9uID0gcGxvdF9kYXRhJFVNQVBfMSA+IDUgfCBwbG90X2RhdGEkVU1BUF8yID4gNQpwbG90X2RhdGEkc2VsZWN0aW9uX2ZjdHIgPSBmYWN0b3IocGxvdF9kYXRhJHNlbGVjdGlvbiwgbGV2ZWxzPWMoVFJVRSwgRkFMU0UpKQoKbXljb2xvdXJzID0gYygicmVkIiwgImdyZXkiKQpuYW1lcyhteWNvbG91cnMpID0gYyhUUlVFLCBGQUxTRSkKCnAgPSBnZ3Bsb3QocGxvdF9kYXRhKSArIGFlc19zdHJpbmcoeD0iVU1BUF8xIiwgeT0iVU1BUF8yIiwgY29sb3VyPSJzZWxlY3Rpb25fZmN0ciIpICsgCiAgICAgICAgICAgZ2VvbV9wb2ludChzaXplPTAuMjUpICsgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9bXljb2xvdXJzKSArCiAgICAgICAgICAgdGhlbWVfY293cGxvdCgpCnByaW50KHApCnNldV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSRjZWxsdHlwZVtyb3duYW1lcyhzZXVfaW50ZWdyYXRlZEBtZXRhLmRhdGEpICVpbiUgcm93bmFtZXMocGxvdF9kYXRhKVtwbG90X2RhdGEkc2VsZWN0aW9uXV0gPSBOQQpVTUFQUGxvdChzZXVfaW50ZWdyYXRlZCwgZ3JvdXAuYnk9IlBoYXNlIikKCnBsb3RfZGF0YSA9IGFzLmRhdGEuZnJhbWUoc2V1X2ludGVncmF0ZWRAcmVkdWN0aW9ucyR1bWFwQGNlbGwuZW1iZWRkaW5ncykKcGxvdF9kYXRhJGNlbGx0eXBlID0gc2V1X2ludGVncmF0ZWRAbWV0YS5kYXRhJGNlbGx0eXBlCnBsb3RfZGF0YSRjZWxsdHlwZSA9IHVubGlzdChsYXBwbHkocGxvdF9kYXRhJGNlbGx0eXBlLCBmdW5jdGlvbih4KSB7IHBhc3RlKHRvdXBwZXIoc3Vic3RyKHgsIDEsIDEpKSwgc3Vic3RyKHgsIDIsIG5jaGFyKHgpKSwgc2VwPSIiKSB9KSkKcGxvdF9kYXRhJGNlbGx0eXBlX2ZjdHIgPSBmYWN0b3IocGxvdF9kYXRhJGNlbGx0eXBlLCBsZXZlbHM9YygiS2VyYXRpbm9jeXRlIiwgIkZpYnJvYmxhc3QiLCAiSW1tdW5lIiwgIkVuZG90aGVsaWFsIikpCm15Y29sb3VycyA9IGJyZXdlci5wYWwoNCwgIkRhcmsyIikKcCA9IGdncGxvdChwbG90X2RhdGEpICsgCiAgYWVzKHg9VU1BUF8xLCB5PVVNQVBfMiwgY29sb3VyPWNlbGx0eXBlX2ZjdHIpICsgCiAgZ2VvbV9wb2ludChzaXplPTAuMikgKyAKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1teWNvbG91cnMsIG5hLnZhbHVlPSJncmV5IikgKwogIHhsYWIoIlVNQVAgMSIpICsgeWxhYigiVU1BUCAyIikKcCA9IHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QocCwgIkNlbGwgdHlwZSIpCnByaW50KHApCgpjb3dwbG90OjpzYXZlX3Bsb3QocGxvdD1wLAogICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAoZmlsZXByZWZpeCwgImZpZ3VyZTVkLnBkZiIpLAogICAgICAgICAgICAgICAgICAgYmFzZV9oZWlnaHQ9NCwKICAgICAgICAgICAgICAgICAgIGJhc2Vfd2lkdGg9NikKYGBgClBsb3QgTm90Y2gxIGV4cHJlc3Npb24gcGVyIGxpYnJhcnkKYGBge3J9Cm5vdGNoMV9leHByID0gZGF0YS5mcmFtZShleHByZXNzaW9uPXRwbVsiTm90Y2gxIiwgcm93bmFtZXMoc2V1X2ludGVncmF0ZWRAbWV0YS5kYXRhKV0sIGxpYnJhcnk9c2V1X2ludGVncmF0ZWRAbWV0YS5kYXRhJGxpYnJhcnkpCm5vdGNoMV9leHByJGxpYnJhcnkgPSBmYWN0b3Iobm90Y2gxX2V4cHIkbGlicmFyeSwgbGV2ZWxzPWMoIldUIDEiLCAiV1QgMiIsICJIT00gS08gMSIsICJIT00gS08gMiIpKQpwID0gZ2dwbG90KG5vdGNoMV9leHByKSArIGFlcyh4PWxpYnJhcnksIHk9ZXhwcmVzc2lvbiwgZmlsbD1saWJyYXJ5KSArIGdlb21fYm94cGxvdCgpICsgCiAgdGhlbWVfY293cGxvdCgpICsgeGxhYigiTGlicmFyeSIpICsgeWxhYigiVFBNIikKcCA9IHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QocCwgIkxpYnJhcnkiLCBsZWdlbmRfYWVzID0gMSkKcHJpbnQocCkKCmNvd3Bsb3Q6OnNhdmVfcGxvdChwbG90PXAsCiAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9IHBhc3RlMChmaWxlcHJlZml4LCAic3VwcGxmaWdfbm90Y2gxX2V4cHJlc3Npb25fYWxsX2NlbGxzLnBkZiIpLAogICAgICAgICAgICAgICAgICAgYmFzZV9oZWlnaHQ9NCwKICAgICAgICAgICAgICAgICAgIGJhc2Vfd2lkdGg9NikKYGBgCiMjIFNhdmUKYGBge3J9CnNhdmUoZmlsZT1wYXN0ZTAoZmlsZXByZWZpeCwgIm5vdGNoMV9iYXRjaF9lZmZlY3Rfd2l0aF9maWx0ZXJfd2l0aF9hZGp1c3RtZW50LlJEYXRhIiksIHNldV9pbnRlZ3JhdGVkLCBtYXJrZXJzX2FsbF9jZWxscykKbWV0ZGF0YSA9IHNldV9pbnRlZ3JhdGVkQG1ldGEuZGF0YQptZXRkYXRhJGNlbGwgPSByb3duYW1lcyhtZXRkYXRhKQp3cml0ZS50YWJsZShtZXRkYXRhLCBmaWxlPXBhc3RlMChmaWxlcHJlZml4LCAibm90Y2gxX2NlbGx0eXBlc19hbmRfbWV0YWRhdGEudHh0IiksIHF1b3RlPUYsIHNlcD0iXHQiLCByb3cubmFtZXM9RikKYGBgCgojIyBDZWxsIHR5cGUgY291bnRzIHBlciBsaWJyYXJ5CmBgYHtyfQpjZWxsdHlwZV9jb3VudCA9IHNldV9pbnRlZ3JhdGVkQG1ldGEuZGF0YVssIGMoIm9yaWcuaWRlbnQiLCAiY2VsbHR5cGUiKV0gJT4lIGdyb3VwX2J5KG9yaWcuaWRlbnQsIGNlbGx0eXBlKSAlPiUgc3VtbWFyaXNlKG49bigpKSAlPiUgc3ByZWFkKGNlbGx0eXBlLCBuKQpjZWxsdHlwZV9mcmFjID0gY2VsbHR5cGVfY291bnRbLCAyOm5jb2woY2VsbHR5cGVfY291bnQpXSAvIHJvd1N1bXMoY2VsbHR5cGVfY291bnRbLCAyOm5jb2woY2VsbHR5cGVfY291bnQpXSwgbmEucm09VCkKY2VsbHR5cGVfZnJhYyA9IGNiaW5kKGRhdGEuZnJhbWUobGlicmFyeT1jKCJXVCAxIiwgIldUIDIiLCAiSE9NIEtPIDEiLCAiSE9NIEtPIDIiKSksIGNlbGx0eXBlX2ZyYWMpCmNlbGx0eXBlX2ZyYWMKd3JpdGUudGFibGUoY2VsbHR5cGVfZnJhYywgZmlsZT1wYXN0ZTAoZmlsZXByZWZpeCwgIm5vdGNoMV9jZWxsX3R5cGVfY291bnRzX3Blcl9saWJyYXJ5LnR4dCIpLCBxdW90ZT1GLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUYpCgoKY2VsbHR5cGVfZnJhYyRsaWJyYXJ5ID0gZmFjdG9yKGNlbGx0eXBlX2ZyYWMkbGlicmFyeSwgbGV2ZWxzPWMoIldUIDEiLCAiV1QgMiIsICJIT00gS08gMSIsICJIT00gS08gMiIpKQpjZWxsX3R5cGVfZnJhY193aWRlciA9IGNlbGx0eXBlX2ZyYWMgJT4lIHBpdm90X2xvbmdlcighbGlicmFyeSkKY2VsbF90eXBlX2ZyYWNfd2lkZXIkbmFtZSA9IHVubGlzdChsYXBwbHkoY2VsbF90eXBlX2ZyYWNfd2lkZXIkbmFtZSwgZnVuY3Rpb24oeCkgeyBwYXN0ZSh0b3VwcGVyKHN1YnN0cih4LCAxLCAxKSksIHN1YnN0cih4LCAyLCBuY2hhcih4KSksIHNlcD0iIikgfSkpCmNlbGxfdHlwZV9mcmFjX3dpZGVyJG5hbWUgPSBmYWN0b3IoY2VsbF90eXBlX2ZyYWNfd2lkZXIkbmFtZSwgbGV2ZWxzPWxldmVscyhwbG90X2RhdCRjZWxsdHlwZV9mY3RyKSkKcCA9IGdncGxvdChjZWxsX3R5cGVfZnJhY193aWRlcikgKyAKICBhZXMoeD1saWJyYXJ5LCB5PXZhbHVlLCBmaWxsPW5hbWUpICsgCiAgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1teWNvbG91cnMsIG5hLnZhbHVlPSJncmV5IikgKwogIHhsYWIoIkxpYnJhcnkiKSArIHlsYWIoIlByb3BvcnRpb24gb2YgY2VsbHMgcGVyIGxpYnJhcnkiKQpwID0gdGhlbWVfcHVibGljYXRpb25fcGxvdChwLCAiQ2VsbCB0eXBlIikKcHJpbnQocCkKY293cGxvdDo6c2F2ZV9wbG90KHBsb3Q9cCwKICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gcGFzdGUwKGZpbGVwcmVmaXgsICJmaWd1cmU1ZV9hbHQxLnBkZiIpLAogICAgICAgICAgICAgICAgICAgYmFzZV9oZWlnaHQ9NCwKICAgICAgICAgICAgICAgICAgIGJhc2Vfd2lkdGg9NikKCnAgPSBnZ3Bsb3QoY2VsbF90eXBlX2ZyYWNfd2lkZXIpICsgCiAgYWVzKHg9bGlicmFyeSwgeT12YWx1ZSwgZmlsbD1uYW1lKSArIAogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHJldmVyc2UgPSBUUlVFKSkgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9bXljb2xvdXJzLCBuYS52YWx1ZT0iZ3JleSIpICsKICB4bGFiKCJMaWJyYXJ5IikgKyB5bGFiKCJQcm9wb3J0aW9uIG9mIGNlbGxzIHBlciBsaWJyYXJ5IikKcCA9IHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QocCwgIkNlbGwgdHlwZSIpCnByaW50KHApCmNvd3Bsb3Q6OnNhdmVfcGxvdChwbG90PXAsCiAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9IHBhc3RlMChmaWxlcHJlZml4LCAiZmlndXJlNWVfYWx0Mi5wZGYiKSwKICAgICAgICAgICAgICAgICAgIGJhc2VfaGVpZ2h0PTQsCiAgICAgICAgICAgICAgICAgICBiYXNlX3dpZHRoPTYpCmBgYAoKIyBBbmFseXNpcyBrZXJhdGlub2N5dGVzCgpSZXJ1biBpbnRlZ3JhdGlvbiBiZWNhdXNlIHdlIGFyZSBubyBsb25nZXIgbm93IGluY2x1ZGluZyBjZWxsIGN5Y2xlIGFkanVzdG1lbnQKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQprZXJhdGlub2N5dGVfYmFyY29kZXMgPSByb3duYW1lcyhzZXVfaW50ZWdyYXRlZEBtZXRhLmRhdGEpW3NldV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSRjZWxsdHlwZT09ImtlcmF0aW5vY3l0ZSIgJiAhaXMubmEoc2V1X2ludGVncmF0ZWRAbWV0YS5kYXRhJGNlbGx0eXBlKV0KCiMgSW5pdGlhbCBydW4gZm91bmQgdGhlc2UgY2VsbHMgYXMgYW4gb3V0bGllciBjbHVzdGVyIGFuZCBleHByZXNzaW5nIGZpYnJvYmxhc3QgbWFya2Vycy4gSGVyZSB0aGVzZSBhcmUgYWxzbyByZW1vdmVkCm1pc2lkZW50aWZpZWRfZmlicm9ibGFzdHMgPSByZWFkLnRhYmxlKCIyMDIwMTIxMV9maWJyb2JsYXN0X2lkZW50aWZpZWRfYXNfa2VyYXRpbm9jeXRlLnR4dCIsIGhlYWRlcj1ULCBzdHJpbmdzQXNGYWN0b3JzPUYpCmtlcmF0aW5vY3l0ZV9iYXJjb2RlcyA9IGtlcmF0aW5vY3l0ZV9iYXJjb2Rlc1trZXJhdGlub2N5dGVfYmFyY29kZXMgJWluJSBtaXNpZGVudGlmaWVkX2ZpYnJvYmxhc3RzJGNlbGxbIW1pc2lkZW50aWZpZWRfZmlicm9ibGFzdHMkZmlicm9ibGFzdF9pZGVudGlmaWVkX2FzX2tlcmF0aW5vY3l0ZV1dCgpvdXRsaWVyX2NlbGxzID0gcmVhZC50YWJsZSgiMjAyMDEyMTFfbm90Y2gxX2NsdXN0ZXJfYXNzaWdubWVudHNfdG9fcmVtb3ZlX291dGxpZXJfY2VsbHMudHh0IiwgaGVhZGVyPVQsIHN0cmluZ3NBc0ZhY3RvcnM9RikKb3V0bGllcl9jZWxscyA9IG91dGxpZXJfY2VsbHNbb3V0bGllcl9jZWxscyRpc19vdXRsaWVyLF0Ka2VyYXRpbm9jeXRlX2JhcmNvZGVzID0ga2VyYXRpbm9jeXRlX2JhcmNvZGVzWyFrZXJhdGlub2N5dGVfYmFyY29kZXMgJWluJSBvdXRsaWVyX2NlbGxzJGNlbGxdCgpzZXVfa2VyYSA9IGxpc3QoKQpmb3IgKGkgaW4gMTpsZW5ndGgoZXhwcl9tYXRyaWNlcykpIHsKICBzZXVfa2VyYVtbaV1dID0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IGV4cHJfbWF0cmljZXNbW2ldXVssIGNvbG5hbWVzKGV4cHJfbWF0cmljZXNbW2ldXSkgJWluJSBrZXJhdGlub2N5dGVfYmFyY29kZXNdLCBtaW4uY2VsbHMgPSAzMCwgbWluLmZlYXR1cmVzID0gMzAwMCkKICAKICBtaXRvLmdlbmVzIDwtIHJvd25hbWVzKEdldEFzc2F5RGF0YShvYmplY3Q9c2V1X2tlcmFbW2ldXSkpW2dyZXBsKHBhdHRlcm4gPSAiXk1ULSIsIHggPSB0b3VwcGVyKHJvd25hbWVzKEdldEFzc2F5RGF0YShvYmplY3Q9c2V1X2tlcmFbW2ldXSkpKSldCiAgcGVyY2VudC5taXRvIDwtIE1hdHJpeDo6Y29sU3VtcyhHZXRBc3NheURhdGEob2JqZWN0PXNldV9rZXJhW1tpXV0sIHNsb3Q9ImNvdW50cyIpW21pdG8uZ2VuZXMsIF0pIC8gTWF0cml4Ojpjb2xTdW1zKEdldEFzc2F5RGF0YShvYmplY3Q9c2V1X2tlcmFbW2ldXSwgc2xvdD0iY291bnRzIikpCiAgc2V1X2tlcmFbW2ldXSA8LSBBZGRNZXRhRGF0YShvYmplY3QgPSBzZXVfa2VyYVtbaV1dLCBtZXRhZGF0YSA9IHBlcmNlbnQubWl0bywgY29sLm5hbWUgPSAicHJvcC5tdCIpCiAgCiAgIyBSZXN0cmljdCBwcm9wb3J0aW9uIE1UIGV4cHJlc3Npb24gdG8gcmVtb3ZlIHVuaGFwcHkgY2VsbHMKICBzZXVfa2VyYVtbaV1dID0gc3Vic2V0KHggPSBzZXVfa2VyYVtbaV1dLCBzdWJzZXQgPSBwcm9wLm10ID4gMC4wMyAmIHByb3AubXQgPCAwLjEpCiAgCiAgIyBSZW1vdmUgcG90ZW50aWFsIGRvdWJsZXRzCiAgc2V1W1tpXV0gPSBzdWJzZXQoeCA9IHNldVtbaV1dLCBzdWJzZXQgPSBuRmVhdHVyZV9STkEgPCA2NTAwICYgbkNvdW50X1JOQSA8IDU1MDAwKQogIAogIHNldV9rZXJhW1tpXV0gPSBTQ1RyYW5zZm9ybShzZXVfa2VyYVtbaV1dLCB2ZXJib3NlID0gRkFMU0UsIHZhcnMudG8ucmVncmVzcz1jKCJwcm9wLm10IiwgIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIikpCiAgc2V1X2tlcmFbW2ldXSA9IENlbGxDeWNsZVNjb3Jpbmcoc2V1X2tlcmFbW2ldXSwgcy5mZWF0dXJlcyA9IGNjLmdlbmVzJHMuZ2VuZXMsIGcybS5mZWF0dXJlcyA9IGNjLmdlbmVzJGcybS5nZW5lcywgYXNzYXkgPSAnU0NUJywgc2V0LmlkZW50ID0gVFJVRSkKICAjIEVuYWJsZSB0aGlzIHdoZW4gYWxzbyBhZGp1c3RpbmcgZm9yIGNlbGwgY3ljbGUKICAjIHNldVtbaV1dID0gU0NUcmFuc2Zvcm0oc2V1W1tpXV0sIHZlcmJvc2UgPSBGQUxTRSwgdmFycy50by5yZWdyZXNzPWMoInByb3AubXQiLCAibkZlYXR1cmVfUk5BIiwgIm5Db3VudF9STkEiLCAiUy5TY29yZSIsICJHMk0uU2NvcmUiKSkKICAKICBzZXVbW2ldXSA9IEFkZE1ldGFEYXRhKG9iamVjdD1zZXVbW2ldXSwgbWV0YWRhdGE9cmVwKGxpYnJhcnlfbmFtZXNbaV0sIGxlbmd0aChzZXVbW2ldXSRuQ291bnRfUk5BKSksIGNvbC5uYW1lPSJsaWJyYXJ5IikKfQpgYGAKUmVydW4gdGhlIGludGVncmF0aW9uIHdpdGggdGhlIG5ld2x5IGFkanVzdGVkIHZhbHVlcwpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgVGhpcyBpcyByZXF1aXJlZCBmb3IgUHJlcFNDVEludGVncmF0aW9uLCBhbiBpbmNyZWFzZSBmcm9tIHRoZSBSIGRlZmF1bHQgNTEyIHRvIDUxMjAgZm9yIHRoaXMgZGF0YXNldApvcHRpb25zKGZ1dHVyZS5nbG9iYWxzLm1heFNpemU9NTEyMCoxMDI0XjIpCgpzZXVfZmVhdHVyZXMgPSBTZWxlY3RJbnRlZ3JhdGlvbkZlYXR1cmVzKG9iamVjdC5saXN0ID0gc2V1X2tlcmEsIG5mZWF0dXJlcyA9IDMwMDApCnNldV9rZXJhID0gUHJlcFNDVEludGVncmF0aW9uKG9iamVjdC5saXN0ID0gc2V1X2tlcmEsIGFuY2hvci5mZWF0dXJlcyA9IHNldV9mZWF0dXJlcywgdmVyYm9zZSA9IEZBTFNFKQpzZXVfa2VyYSA9IGxhcHBseShYID0gc2V1X2tlcmEsIEZVTiA9IFJ1blBDQSwgdmVyYm9zZSA9IEZBTFNFLCBmZWF0dXJlcyA9IHNldV9mZWF0dXJlcykKc2V1X2FuY2hvcnMgPSBGaW5kSW50ZWdyYXRpb25BbmNob3JzKG9iamVjdC5saXN0ID0gc2V1X2tlcmEsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIlNDVCIsIGFuY2hvci5mZWF0dXJlcyA9IHNldV9mZWF0dXJlcywgdmVyYm9zZSA9IEZBTFNFLCByZWR1Y3Rpb24gPSAicnBjYSIpCnNldV9rZXJhX2ludGVncmF0ZWQgPSBJbnRlZ3JhdGVEYXRhKGFuY2hvcnNldCA9IHNldV9hbmNob3JzLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJTQ1QiLCB2ZXJib3NlID0gRkFMU0UpCmxpYnJhcnlfcGVyX2NlbGwgPSBmYWN0b3IodW5saXN0KGxhcHBseShzZXUsIGZ1bmN0aW9uKHgpIHhAbWV0YS5kYXRhJGxpYnJhcnkpKSwgbGV2ZWxzPWxpYnJhcnlfbmFtZXMpCm5hbWVzKGxpYnJhcnlfcGVyX2NlbGwpID0gdW5saXN0KGxhcHBseShzZXUsIGZ1bmN0aW9uKHgpIHJvd25hbWVzKHhAbWV0YS5kYXRhKSkpCnNldV9rZXJhX2ludGVncmF0ZWQgPSBBZGRNZXRhRGF0YShvYmplY3Q9c2V1X2tlcmFfaW50ZWdyYXRlZCwgbWV0YWRhdGE9bGlicmFyeV9wZXJfY2VsbCwgY29sLm5hbWU9ImxpYnJhcnkiKQpgYGAKCmBgYHtyfQpzZXVfa2VyYV9pbnRlZ3JhdGVkID0gUnVuUENBKHNldV9rZXJhX2ludGVncmF0ZWQsIHZlcmJvc2UgPSBGQUxTRSkKClZsblBsb3Qoc2V1X2tlcmFfaW50ZWdyYXRlZCwgZ3JvdXAuYnk9Im9yaWcuaWRlbnQiLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiwgInByb3AubXQiKSwgbmNvbCA9IDMsIHB0LnNpemU9MC4wMSkKRWxib3dQbG90KHNldV9rZXJhX2ludGVncmF0ZWQsIG5kaW1zPTUwKQpgYGAKSGVyZSB3ZSBkbyB0YWtlIHRoZSBwb2ludCB3aGVyZSB0aGUgYWRkaXRpb25hbCB2YXJpYXRpb24gdGFpbCBpbiB0aGUgYWJvdmUgcGxvdCBiZWNvbWVzIGZsYXQuIFBpY2tlZCAxMCBQQ3MuCmBgYHtyfQpzZXVfa2VyYV9pbnRlZ3JhdGVkID0gUnVuVU1BUChzZXVfa2VyYV9pbnRlZ3JhdGVkLCBkaW1zID0gMToxMCkKVU1BUFBsb3Qoc2V1X2tlcmFfaW50ZWdyYXRlZCkKcCA9IFVNQVBQbG90KHNldV9rZXJhX2ludGVncmF0ZWQsZ3JvdXAuYnk9ImxpYnJhcnkiKQpwID0gdGhlbWVfcHVibGljYXRpb25fcGxvdChwLCAiTGlicmFyeSIpCnByaW50KHApCmNvd3Bsb3Q6OnNhdmVfcGxvdChwbG90PXAsCiAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9IHBhc3RlMChmaWxlcHJlZml4LCAic3VwcGxmaWdfdW1hcF9rZXJhdGlub2N5dGVzX292ZXJsYXlfbGlicmFyeS5wZGYiKSwKICAgICAgICAgICAgICAgICAgIGJhc2VfaGVpZ2h0PTQsCiAgICAgICAgICAgICAgICAgICBiYXNlX3dpZHRoPTYpCgoKVU1BUFBsb3Qoc2V1X2tlcmFfaW50ZWdyYXRlZCxncm91cC5ieT0iUGhhc2UiKQoKcGxvdF9kYXQgPSBhcy5kYXRhLmZyYW1lKHNldV9rZXJhX2ludGVncmF0ZWRAcmVkdWN0aW9uc1tbInVtYXAiXV1AY2VsbC5lbWJlZGRpbmdzKQpwbG90X2RhdCRrcnQxNCA9IGxvZyh0cG1bIktydDE0Iiwgcm93bmFtZXMoc2V1X2tlcmFfaW50ZWdyYXRlZEBtZXRhLmRhdGEpXSkKcGxvdF9kYXQkdGdtMyA9IGxvZyh0cG1bIlRnbTMiLCByb3duYW1lcyhzZXVfa2VyYV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSldKQpwbG90X2RhdCRrcnQ0ID0gbG9nKHRwbVsiS3J0NCIsIHJvd25hbWVzKHNldV9rZXJhX2ludGVncmF0ZWRAbWV0YS5kYXRhKV0pCnBsb3RfZGF0JGxvciA9IGxvZyh0cG1bIkxvciIsIHJvd25hbWVzKHNldV9rZXJhX2ludGVncmF0ZWRAbWV0YS5kYXRhKV0pCgpwMSA9IHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QoZ2dwbG90KHBsb3RfZGF0KSArIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIsIGNvbG91cj1rcnQxNCkgKyBnZW9tX3BvaW50KHNpemU9MC4yKSArIHhsYWIoIlVNQVAgMSIpICsgeWxhYigiVU1BUCAyIikgKyB0aGVtZV9jb3dwbG90KCkgKyBnZ3RpdGxlKCJLcnQxNCIpLCBsZWdlbmRfdGl0bGUgPSAibG9nKFRQTSkiKQpwMiA9IHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QoZ2dwbG90KHBsb3RfZGF0KSArIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIsIGNvbG91cj10Z20zKSArIGdlb21fcG9pbnQoc2l6ZT0wLjIpICsgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSArIHRoZW1lX2Nvd3Bsb3QoKSArIGdndGl0bGUoIlRnbTMiKSwgbGVnZW5kX3RpdGxlID0gImxvZyhUUE0pIikKcDMgPSB0aGVtZV9wdWJsaWNhdGlvbl9wbG90KGdncGxvdChwbG90X2RhdCkgKyBhZXMoeD1VTUFQXzEsIHk9VU1BUF8yLCBjb2xvdXI9a3J0NCkgKyBnZW9tX3BvaW50KHNpemU9MC4yKSArIHhsYWIoIlVNQVAgMSIpICsgeWxhYigiVU1BUCAyIikgKyB0aGVtZV9jb3dwbG90KCkgKyBnZ3RpdGxlKCJLcnQ0IiksIGxlZ2VuZF90aXRsZSA9ICJsb2coVFBNKSIpCnA0ID0gdGhlbWVfcHVibGljYXRpb25fcGxvdChnZ3Bsb3QocGxvdF9kYXQpICsgYWVzKHg9VU1BUF8xLCB5PVVNQVBfMiwgY29sb3VyPWxvcikgKyBnZW9tX3BvaW50KHNpemU9MC4yKSArIHhsYWIoIlVNQVAgMSIpICsgeWxhYigiVU1BUCAyIikgKyB0aGVtZV9jb3dwbG90KCkgKyBnZ3RpdGxlKCJMb3IiKSwgbGVnZW5kX3RpdGxlID0gImxvZyhUUE0pIikKcCA9IHBsb3RfZ3JpZChwMSwgcDIsIHAzLCBwNCwgbmNvbD0yKQpwcmludChwKQpjb3dwbG90OjpzYXZlX3Bsb3QocGxvdD1wLAogICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAoZmlsZXByZWZpeCwgInN1cHBsZmlnX2tlcmF0aW5vY3l0ZXNfbWFya2VyX2V4cHJlc3Npb24ucGRmIiksCiAgICAgICAgICAgICAgICAgICBiYXNlX2hlaWdodD00LAogICAgICAgICAgICAgICAgICAgYmFzZV93aWR0aD02KQoKVmxuUGxvdChzZXVfa2VyYV9pbnRlZ3JhdGVkLCBncm91cC5ieT0ib3JpZy5pZGVudCIsIGZlYXR1cmVzID0gInByb3AubXQiLCBuY29sID0gMSwgcHQuc2l6ZT0wLjAxKQoKRmVhdHVyZVBsb3Qoc2V1X2tlcmFfaW50ZWdyYXRlZCwgZmVhdHVyZXM9YygibkZlYXR1cmVfUk5BIiwgIm5Db3VudF9STkEiLCAicHJvcC5tdCIpKQoKcGxvdF9kYXQgPSBhcy5kYXRhLmZyYW1lKHNldV9rZXJhX2ludGVncmF0ZWRAcmVkdWN0aW9ucyR1bWFwQGNlbGwuZW1iZWRkaW5ncykKcGxvdF9kYXQkcGhhc2UgPSBzZXVfa2VyYV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSRQaGFzZQpwbG90X2RhdCRwaGFzZSA9IGZhY3RvcihwbG90X2RhdCRwaGFzZSwgbGV2ZWxzPWMoIkcxIiwgIkcyTSIsICJTIikpCiMgbXljb2xvdXJzID0gYnJld2VyLnBhbCg0LCAiRGFyazIiKQpwID0gZ2dwbG90KHBsb3RfZGF0KSArIAogIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIsIGNvbG91cj1waGFzZSkgKyAKICBnZW9tX3BvaW50KHNpemU9MC4yKSArIAogIHhsYWIoIlVNQVAgMSIpICsgeWxhYigiVU1BUCAyIikKcCA9IHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QocCwgIkNlbGwgdHlwZSIpCnByaW50KHApCgpjb3dwbG90OjpzYXZlX3Bsb3QocGxvdD1wLAogICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAoZmlsZXByZWZpeCwgImZpZ3VyZTVoLnBkZiIpLAogICAgICAgICAgICAgICAgICAgYmFzZV9oZWlnaHQ9NCwKICAgICAgICAgICAgICAgICAgIGJhc2Vfd2lkdGg9NikKCnBoYXNlX2NvdW50ID0gc2V1X2tlcmFfaW50ZWdyYXRlZEBtZXRhLmRhdGEgJT4lIGdyb3VwX2J5KGxpYnJhcnksIFBoYXNlKSAlPiUgc3VtbWFyaXNlKG49bigpKSAlPiUgbXV0YXRlKGZyYWMgPSBuL3N1bShuKSkKcGhhc2VfY291bnQkbGlicmFyeSA9IGZhY3RvcihwaGFzZV9jb3VudCRsaWJyYXJ5LCBsZXZlbHM9YygiV1QgMSIsICJXVCAyIiwgIkhPTSBLTyAxIiwgIkhPTSBLTyAyIikpCnAgPSBnZ3Bsb3QocGhhc2VfY291bnQpICsgCiAgYWVzKHg9bGlicmFyeSwgeT1mcmFjLCBmaWxsPVBoYXNlKSArIAogIGdlb21fYmFyKHBvc2l0aW9uPSJkb2RnZSIsIHN0YXQ9ImlkZW50aXR5IikgKyAKICB4bGFiKCJMaWJyYXJ5IikgKyB5bGFiKCJQcm9wb3J0aW9uIG9mIGNlbGxzIHBlciBsaWJyYXJ5IikKcCA9IHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QocCwgIkN5Y2xlIHBoYXNlIikKcHJpbnQocCkKY293cGxvdDo6c2F2ZV9wbG90KHBsb3Q9cCwKICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gcGFzdGUwKGZpbGVwcmVmaXgsICJmaWd1cmU1aV9hbHQxLnBkZiIpLAogICAgICAgICAgICAgICAgICAgYmFzZV9oZWlnaHQ9NCwKICAgICAgICAgICAgICAgICAgIGJhc2Vfd2lkdGg9NikKCnAgPSBnZ3Bsb3QocGhhc2VfY291bnQpICsgCiAgYWVzKHg9bGlicmFyeSwgeT1mcmFjLCBmaWxsPVBoYXNlKSArIAogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHJldmVyc2UgPSBUUlVFKSkgKyAKICB4bGFiKCJMaWJyYXJ5IikgKyB5bGFiKCJQcm9wb3J0aW9uIG9mIGNlbGxzIHBlciBsaWJyYXJ5IikKcCA9IHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QocCwgIkN5Y2xlIHBoYXNlIikKcHJpbnQocCkKY293cGxvdDo6c2F2ZV9wbG90KHBsb3Q9cCwKICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gcGFzdGUwKGZpbGVwcmVmaXgsICJmaWd1cmU1aV9hbHQyLnBkZiIpLAogICAgICAgICAgICAgICAgICAgYmFzZV9oZWlnaHQ9NCwKICAgICAgICAgICAgICAgICAgIGJhc2Vfd2lkdGg9NikKCndyaXRlLnRhYmxlKHBoYXNlX2NvdW50WywgYygibGlicmFyeSIsICJQaGFzZSIsICJmcmFjIildICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tPVBoYXNlLCB2YWx1ZXNfZnJvbT1mcmFjKSwgCiAgICAgICAgICAgIGZpbGU9cGFzdGUwKGZpbGVwcmVmaXgsICJmcmFjX2NlbGxzX2xpYnJhcnlfY2VsbGN5Y2xlLnR4dCIpLCBxdW90ZT1GLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUYpCmBgYAoKYGBge3J9CiMgZ2V0IHRwbSBmb3IgbWFya2VyIGdlbmVzIHBlciBjbHVzdGVyCnNldV9rZXJhX2ludGVncmF0ZWQgPSBGaW5kTmVpZ2hib3JzKHNldV9rZXJhX2ludGVncmF0ZWQsIGRpbXMgPSAxOjEwKQpzZXVfa2VyYV9pbnRlZ3JhdGVkID0gRmluZENsdXN0ZXJzKG9iamVjdCA9IHNldV9rZXJhX2ludGVncmF0ZWQsIHJlc29sdXRpb24gPSAwLjYsIHZlcmJvc2U9RkFMU0UpCm1hcmtlcnNfa2VyYXRpbm9jeXRlcyA9IEZpbmRBbGxNYXJrZXJzKHNldV9rZXJhX2ludGVncmF0ZWQsIG9ubHkucG9zID0gVFJVRSwgbWluLnBjdCA9IDAuMSwgdGhyZXNoLnVzZSA9IDAuMjUsIHZlcmJvc2UgPSBGKQpwbG90IDwtIERpbVBsb3Qob2JqZWN0ID0gc2V1X2tlcmFfaW50ZWdyYXRlZCwgcmVkdWN0aW9uID0gInVtYXAiKQpMYWJlbENsdXN0ZXJzKHBsb3QgPSBwbG90LCBpZCA9ICdpZGVudCcsIHNpemU9NikKd3JpdGUudGFibGUobWFya2Vyc19rZXJhdGlub2N5dGVzLCBmaWxlPXBhc3RlMChmaWxlcHJlZml4LCAic2lnbmlmX2V4cHJfbWFya2Vyc19rZXJhdGlub2N5dGVfYW5hbHlzaXMudHh0IiksIHF1b3RlPUYsIHNlcD0iXHQiLCByb3cubmFtZXM9RikKYGBgCgojIyBEZXRlcm1pbmUgbnVtYmVyIG9mIGJhc2FsIGNlbGxzCgpUaGUgJ2NpcmNsZScgaXMgZm9ybWVkIG9mIGJhc2FsIGNlbGxzLCB0aGUgdGFpbCBvZiBzdXByYWJhc2FsLiBIZXJlIHdlIG1ha2UgdGhlIG9ic2VydmF0aW9uIHRoYXQga25vd24gbWFya2VyIGdlbmVzIGJyb2FkbHkgZGVmaW5lIHRoZSBjdXRvZmYgcG9pbnQsIHdoaWNoIHdlIGFwcHJveGltYXRlIHdpdGggdGhlIHJpZ2h0IGJlbG93IGRlZmluZWQgc2xvcGUvaW50ZXJjZXB0IG9mIGEgbGluZS4gV2hhdCB3ZSByZWFsbHkgd2FudCB0byBrbm93IGlzIHdoZXRoZXIgbGlicmFyaWVzIGFyZSBlcXVhbGx5IHJlcHJlc2VudGVkIG9uIGJvdGggc2lkZXMgb2YgdGhlIGxpbmUuIFdlJ3JlIGxlc3MgY29uY2VybmVkIHdpdGggd2hldGhlciB0aGlzIGlzIHRoZSBvcHRpbWFsIHNlcGFyYXRpb24gb2YgYmFzYWwgYW5kIHN1cHJhYmFzYWwgY2VsbHMsIGVzcGVjaWFsbHkgZ2l2ZW4gaXQgc2VlbXMgbGlrZWx5IGRpZmZlcmVudGlhdGVkIGNlbGxzIGFyZSBsZXNzIGxpa2VseSB0byBtYWtlIGl0IHRocm91Z2ggdGhlIHNjUk5Bc2VxIHByb3RvY29scyBkdWUgdG8gdGhlaXIgY2hhcmFjdGVyaXN0aWNzLgpgYGB7cn0KIyBlc3RhYmxpc2hlZCB0byBicm9hZGx5IGNhcHR1cmUgdGhlIGNoYW5nZW92ZXIgcG9pbnQgd2hlcmUgS3J0MTQgZHJvcHMgb2YgYW5kIFRnbTMgc3RhcnRzIGdvaW5nIHVwCnNsb3BlID0gMS41CmludGVyY2VwdCA9IDUuNQoKYWRkX3RocmVzaG9sZCA9IGZ1bmN0aW9uKHAsIHNsb3BlLCBpbnRlcmNlcHQpIHsKICByZXR1cm4ocCArIGdlb21fYWJsaW5lKHNsb3BlPXNsb3BlLCBpbnRlcmNlcHQ9aW50ZXJjZXB0LCBjb2xvdXI9ImJsYWNrIiwgbGluZXR5cGU9MikpCn0KCnBjMSA9IGFzLmRhdGEuZnJhbWUoc2V1X2tlcmFfaW50ZWdyYXRlZEByZWR1Y3Rpb25zW1sidW1hcCJdXUBjZWxsLmVtYmVkZGluZ3NbLCBjKDEsIDIpLCBkcm9wPUZdKQpwYzEkbGlicmFyeSA9IGFzLmNoYXJhY3RlcihzZXVfa2VyYV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSRsaWJyYXJ5W21hdGNoKHJvd25hbWVzKHBjMSksIHJvd25hbWVzKHNldV9rZXJhX2ludGVncmF0ZWRAbWV0YS5kYXRhKSldKQpwYzEkc2VsZWN0ZWQgPSBpZmVsc2UocGMxJFVNQVBfMiA+IChpbnRlcmNlcHQrc2xvcGUqcGMxJFVNQVBfMSksIDAsIDEpCnBjMSRzZWxlY3RlZCA9IGZhY3RvcihwYzEkc2VsZWN0ZWQsIGxldmVscz1jKDEsIDApKQpwYzEkbGF5ZXIgPSAiQmFzYWwiCnBjMSRsYXllcltwYzEkc2VsZWN0ZWQ9PSIwIl0gPSAiU3VwcmFiYXNhbCIKcGMxJGxheWVyID0gZmFjdG9yKHBjMSRsYXllciwgbGV2ZWxzPWMoIkJhc2FsIiwgIlN1cHJhYmFzYWwiKSkKYGBgCgpQbG90IHRoZSBleHByZXNzaW9uIG9mIGEgbnVtYmVyIG9mIG1hcmtlcnMgcmVsYXRpdmUgdG8gdGhlIHNlbGVjdGVkIHRocmVzaG9sZCB0byBzaG93IHdlJ3ZlIGJyb2FkbHkgY2FwdHVyZWQgdGhlIGNyb3Nzb3ZlciBhcmVhCmBgYHtyfQpwbG90X2RhdCA9IGFzLmRhdGEuZnJhbWUoc2V1X2tlcmFfaW50ZWdyYXRlZEByZWR1Y3Rpb25zW1sidW1hcCJdXUBjZWxsLmVtYmVkZGluZ3MpCnBsb3RfZGF0JGtydDE0ID0gbG9nKHRwbVsiS3J0MTQiLCByb3duYW1lcyhzZXVfa2VyYV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSldKQpwbG90X2RhdCR0Z20zID0gbG9nKHRwbVsiVGdtMyIsIHJvd25hbWVzKHNldV9rZXJhX2ludGVncmF0ZWRAbWV0YS5kYXRhKV0pCnBsb3RfZGF0JGtydDQgPSBsb2codHBtWyJLcnQ0Iiwgcm93bmFtZXMoc2V1X2tlcmFfaW50ZWdyYXRlZEBtZXRhLmRhdGEpXSkKcGxvdF9kYXQkbG9yID0gbG9nKHRwbVsiTG9yIiwgcm93bmFtZXMoc2V1X2tlcmFfaW50ZWdyYXRlZEBtZXRhLmRhdGEpXSkKCnAxID0gdGhlbWVfcHVibGljYXRpb25fcGxvdChnZ3Bsb3QocGxvdF9kYXQpICsgYWVzKHg9VU1BUF8xLCB5PVVNQVBfMiwgY29sb3VyPWtydDE0KSArIGdlb21fcG9pbnQoc2l6ZT0wLjIpICsgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSArIHRoZW1lX2Nvd3Bsb3QoKSArIGdndGl0bGUoIktydDE0IiksIGxlZ2VuZF90aXRsZSA9ICJsb2coVFBNKSIpCnAyID0gdGhlbWVfcHVibGljYXRpb25fcGxvdChnZ3Bsb3QocGxvdF9kYXQpICsgYWVzKHg9VU1BUF8xLCB5PVVNQVBfMiwgY29sb3VyPXRnbTMpICsgZ2VvbV9wb2ludChzaXplPTAuMikgKyB4bGFiKCJVTUFQIDEiKSArIHlsYWIoIlVNQVAgMiIpICsgdGhlbWVfY293cGxvdCgpICsgZ2d0aXRsZSgiVGdtMyIpLCBsZWdlbmRfdGl0bGUgPSAibG9nKFRQTSkiKQpwMyA9IHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QoZ2dwbG90KHBsb3RfZGF0KSArIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIsIGNvbG91cj1rcnQ0KSArIGdlb21fcG9pbnQoc2l6ZT0wLjIpICsgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSArIHRoZW1lX2Nvd3Bsb3QoKSArIGdndGl0bGUoIktydDQiKSwgbGVnZW5kX3RpdGxlID0gImxvZyhUUE0pIikKcDQgPSB0aGVtZV9wdWJsaWNhdGlvbl9wbG90KGdncGxvdChwbG90X2RhdCkgKyBhZXMoeD1VTUFQXzEsIHk9VU1BUF8yLCBjb2xvdXI9bG9yKSArIGdlb21fcG9pbnQoc2l6ZT0wLjIpICsgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSArIHRoZW1lX2Nvd3Bsb3QoKSArIGdndGl0bGUoIkxvciIpLCBsZWdlbmRfdGl0bGUgPSAibG9nKFRQTSkiKQpwID0gcGxvdF9ncmlkKGFkZF90aHJlc2hvbGQocDEsIHNsb3BlLCBpbnRlcmNlcHQpLCAKICAgICAgICAgICAgICBhZGRfdGhyZXNob2xkKHAyLCBzbG9wZSwgaW50ZXJjZXB0KSwgCiAgICAgICAgICAgICAgYWRkX3RocmVzaG9sZChwMywgc2xvcGUsIGludGVyY2VwdCksIAogICAgICAgICAgICAgIGFkZF90aHJlc2hvbGQocDQsIHNsb3BlLCBpbnRlcmNlcHQpLCBuY29sPTIpCnByaW50KHApCmNvd3Bsb3Q6OnNhdmVfcGxvdChwbG90PXAsCiAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9IHBhc3RlMChmaWxlcHJlZml4LCAic3VwcGxmaWdfYmFzYWxfY2VsbF90aHJlc2hvbGRfdnNfbWFya2Vycy5wZGYiKSwKICAgICAgICAgICAgICAgICAgIGJhc2VfaGVpZ2h0PTQsCiAgICAgICAgICAgICAgICAgICBiYXNlX3dpZHRoPTYpCmBgYAoKUGxvdCB0aGUgdGhyZXNob2xkIHBlciBsaWJyYXJ5IHRvIHNob3cgd2hpY2ggY2VsbHMgaGF2ZSBiZWVuIG1hcmtlZCBhcyBiYXNhbCBjZWxscwpgYGB7cn0KbXljb2xvdXJzID0gYygicmVkIiwgImdyZXkiKQpwMSA9IGdncGxvdChwYzFbcGMxJGxpYnJhcnk9PSJXVCAxIixdKSArCiAgYWVzKHg9VU1BUF8xLCB5PVVNQVBfMiwgY29sb3VyPWxheWVyKSArCiAgZ2VvbV9wb2ludChzaXplPTAuMikgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPW15Y29sb3VycywgbmEudmFsdWU9ImdyZXkiKSArCiAgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSArIGdndGl0bGUoIldUIDEiKSArIHRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCgpwMiA9IGdncGxvdChwYzFbcGMxJGxpYnJhcnk9PSJXVCAyIixdKSArCiAgYWVzKHg9VU1BUF8xLCB5PVVNQVBfMiwgY29sb3VyPWxheWVyKSArCiAgZ2VvbV9wb2ludChzaXplPTAuMikgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPW15Y29sb3VycywgbmEudmFsdWU9ImdyZXkiKSArCiAgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSArIGdndGl0bGUoIldUIDIiKSArIHRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpIAoKcDMgPSBnZ3Bsb3QocGMxW3BjMSRsaWJyYXJ5PT0iSE9NIEtPIDEiLF0pICsKICBhZXMoeD1VTUFQXzEsIHk9VU1BUF8yLCBjb2xvdXI9bGF5ZXIpICsKICBnZW9tX3BvaW50KHNpemU9MC4yKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9bXljb2xvdXJzLCBuYS52YWx1ZT0iZ3JleSIpICsKICB4bGFiKCJVTUFQIDEiKSArIHlsYWIoIlVNQVAgMiIpICsgZ2d0aXRsZSgiSE9NIEtPIDEiKSArIHRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCgpwNCA9IGdncGxvdChwYzFbcGMxJGxpYnJhcnk9PSJIT00gS08gMiIsXSkgKwogIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIsIGNvbG91cj1sYXllcikgKwogIGdlb21fcG9pbnQoc2l6ZT0wLjIpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1teWNvbG91cnMsIG5hLnZhbHVlPSJncmV5IikgKwogIHhsYWIoIlVNQVAgMSIpICsgeWxhYigiVU1BUCAyIikgKyBnZ3RpdGxlKCJIT00gS08gMiIpICsgdGhlbWVfY293cGxvdCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKCnAgPSBwbG90X2dyaWQoYWRkX3RocmVzaG9sZChwMSwgc2xvcGUsIGludGVyY2VwdCksIAogICAgICAgICAgICAgIGFkZF90aHJlc2hvbGQocDIsIHNsb3BlLCBpbnRlcmNlcHQpLCAKICAgICAgICAgICAgICBhZGRfdGhyZXNob2xkKHAzLCBzbG9wZSwgaW50ZXJjZXB0KSwgCiAgICAgICAgICAgICAgYWRkX3RocmVzaG9sZChwNCwgc2xvcGUsIGludGVyY2VwdCkpCnByaW50KHApCmNvd3Bsb3Q6OnNhdmVfcGxvdChwbG90PXAsCiAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9IHBhc3RlMChmaWxlcHJlZml4LCAic3VwcGxmaWdfYmFzYWxfY2VsbF9jbGFzc2lmaWNhdGlvbl9tYXAucGRmIiksCiAgICAgICAgICAgICAgICAgICBiYXNlX2hlaWdodD00LAogICAgICAgICAgICAgICAgICAgYmFzZV93aWR0aD02KQpgYGAKClN1bW1hcmlzZSB0aGUgY2FsbHMgaW50byBhIHRhYmxlCmBgYHtyfQpiYXNhbF9jZWxsX2NvdW50ID0gcGMxICU+JSBncm91cF9ieShzZWxlY3RlZCwgbGlicmFyeSkgJT4lIHN1bW1hcmlzZShuPW4oKSkgJT4lIHBpdm90X3dpZGVyKG5hbWVzX2Zyb209c2VsZWN0ZWQsIHZhbHVlc19mcm9tPW4pCmNvbG5hbWVzKGJhc2FsX2NlbGxfY291bnQpWzI6M10gPSBjKCJudW1fYmFzYWwiLCAibnVtX3N1cHJhYmFzYWwiKQpiYXNhbF9jZWxsX2NvdW50WywgYygiZnJhY19iYXNhbCIsICJmcmFjX3N1cHJhYmFzYWwiKV0gPSBiYXNhbF9jZWxsX2NvdW50WywgMjozXSAvIHJvd1N1bXMoYmFzYWxfY2VsbF9jb3VudFssIDI6M10pCnByaW50KGJhc2FsX2NlbGxfY291bnQpCndyaXRlLnRhYmxlKGJhc2FsX2NlbGxfY291bnQsIGZpbGU9cGFzdGUwKGZpbGVwcmVmaXgsICJub3RjaDFfZnJhY3Rpb25fYmFzYWxfY2VsbHNfdW1hcF90aHJlc2hvbGQudHh0IiksIHF1b3RlPUYsIHNlcD0iXHQiLCByb3cubmFtZXM9RikKYGBgCgpNYWtlIHRoZSBzdW1tYXJ5IGZpZ3VyZXMgZm9yIHRoZSBwYXBlcgpgYGB7cn0KCm15Y29sb3VycyA9IGMoInJlZCIsICJncmV5IikKcCA9IGdncGxvdChwYzEpICsgCiAgYWVzKHg9VU1BUF8xLCB5PVVNQVBfMiwgY29sb3VyPWxheWVyKSArIAogIGdlb21fcG9pbnQoc2l6ZT0wLjIpICsgCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9bXljb2xvdXJzLCBuYS52YWx1ZT0iZ3JleSIpICsKICB4bGFiKCJVTUFQIDEiKSArIHlsYWIoIlVNQVAgMiIpCnAgPSB0aGVtZV9wdWJsaWNhdGlvbl9wbG90KHAsICJMYXllciIpCnByaW50KHApCgpjb3dwbG90OjpzYXZlX3Bsb3QocGxvdD1wLAogICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAoZmlsZXByZWZpeCwgImZpZ3VyZTVmLnBkZiIpLAogICAgICAgICAgICAgICAgICAgYmFzZV9oZWlnaHQ9NCwKICAgICAgICAgICAgICAgICAgIGJhc2Vfd2lkdGg9NikKCmJhc2FsX2NlbGxfY291bnQgPSBiYXNhbF9jZWxsX2NvdW50WyxjKCJsaWJyYXJ5IiwgImZyYWNfYmFzYWwiLCAiZnJhY19zdXByYWJhc2FsIildCmJhc2FsX2NlbGxfY291bnQkbGlicmFyeSA9IGZhY3RvcihiYXNhbF9jZWxsX2NvdW50JGxpYnJhcnksIGxldmVscz1jKCJXVCAxIiwgIldUIDIiLCAiSE9NIEtPIDEiLCAiSE9NIEtPIDIiKSkKY29sbmFtZXMoYmFzYWxfY2VsbF9jb3VudClbMjozXSA9IGMoIkJhc2FsIiwgIlN1cHJhYmFzYWwiKQpwID0gZ2dwbG90KGJhc2FsX2NlbGxfY291bnQgJT4lIHBpdm90X2xvbmdlcighbGlicmFyeSkpICsgCiAgYWVzKHg9bGlicmFyeSwgeT12YWx1ZSwgZmlsbD1uYW1lKSArIAogIGdlb21fYmFyKHBvc2l0aW9uPSJkb2RnZSIsIHN0YXQ9ImlkZW50aXR5IikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9bXljb2xvdXJzLCBuYS52YWx1ZT0iZ3JleSIpICsKICB4bGFiKCJMaWJyYXJ5IikgKyB5bGFiKCJQcm9wb3J0aW9uIG9mIGNlbGxzIHBlciBsaWJyYXJ5IikKcCA9IHRoZW1lX3B1YmxpY2F0aW9uX3Bsb3QocCwgIkxheWVyIikKcHJpbnQocCkKY293cGxvdDo6c2F2ZV9wbG90KHBsb3Q9cCwKICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gcGFzdGUwKGZpbGVwcmVmaXgsICJmaWd1cmU1Z19hbHQxLnBkZiIpLAogICAgICAgICAgICAgICAgICAgYmFzZV9oZWlnaHQ9NCwKICAgICAgICAgICAgICAgICAgIGJhc2Vfd2lkdGg9NikKCnAgPSBnZ3Bsb3QoYmFzYWxfY2VsbF9jb3VudCAlPiUgcGl2b3RfbG9uZ2VyKCFsaWJyYXJ5KSkgKyAKICBhZXMoeD1saWJyYXJ5LCB5PXZhbHVlLCBmaWxsPW5hbWUpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbChyZXZlcnNlID0gVFJVRSkpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15Y29sb3VycywgbmEudmFsdWU9ImdyZXkiKSArCiAgeGxhYigiTGlicmFyeSIpICsgeWxhYigiUHJvcG9ydGlvbiBvZiBjZWxscyBwZXIgbGlicmFyeSIpCnAgPSB0aGVtZV9wdWJsaWNhdGlvbl9wbG90KHAsICJMYXllciIpCnByaW50KHApCmNvd3Bsb3Q6OnNhdmVfcGxvdChwbG90PXAsCiAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9IHBhc3RlMChmaWxlcHJlZml4LCAiZmlndXJlNWdfYWx0Mi5wZGYiKSwKICAgICAgICAgICAgICAgICAgIGJhc2VfaGVpZ2h0PTQsCiAgICAgICAgICAgICAgICAgICBiYXNlX3dpZHRoPTYpCmBgYAojIyBBZGRpdGlvbmFsIHBsb3RzClBsb3QgTm90Y2gxIGV4cHJlc3Npb24gcGVyIGxpYnJhcnkKYGBge3J9Cm5vdGNoMV9leHByID0gZGF0YS5mcmFtZShleHByZXNzaW9uPXRwbVsiTm90Y2gxIiwgcm93bmFtZXMoc2V1X2tlcmFfaW50ZWdyYXRlZEBtZXRhLmRhdGEpXSwgbGlicmFyeT1zZXVfa2VyYV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSRsaWJyYXJ5KQpub3RjaDFfZXhwciRsaWJyYXJ5ID0gZmFjdG9yKG5vdGNoMV9leHByJGxpYnJhcnksIGxldmVscz1jKCJXVCAxIiwgIldUIDIiLCAiSE9NIEtPIDEiLCAiSE9NIEtPIDIiKSkKcCA9IGdncGxvdChub3RjaDFfZXhwcikgKyBhZXMoeD1saWJyYXJ5LCB5PWV4cHJlc3Npb24sIGZpbGw9bGlicmFyeSkgKyBnZW9tX2JveHBsb3QoKSArIAogIHRoZW1lX2Nvd3Bsb3QoKSArIHhsYWIoIkxpYnJhcnkiKSArIHlsYWIoIlRQTSIpCnAgPSB0aGVtZV9wdWJsaWNhdGlvbl9wbG90KHAsICJMaWJyYXJ5IiwgbGVnZW5kX2Flcz0xKQpwcmludChwKQoKY293cGxvdDo6c2F2ZV9wbG90KHBsb3Q9cCwKICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gcGFzdGUwKGZpbGVwcmVmaXgsICJzdXBwbGZpZ19ub3RjaDFfZXhwcmVzc2lvbl9vbmx5X2tlcmF0aW5vY3l0ZXMucGRmIiksCiAgICAgICAgICAgICAgICAgICBiYXNlX2hlaWdodD00LAogICAgICAgICAgICAgICAgICAgYmFzZV93aWR0aD02KQpgYGAKCgpEb3duc2FtcGxlZCBwbG90IHRvIHNob3cgYW4gZXF1YWwgbnVtYmVyIG9mIGNlbGxzIHBlciBsaWJyYXJ5IC0gMTAwMCBjZWxscwpgYGB7cn0KY291bnRzX3Blcl9saWJyYXJ5ID0gdGFibGUoc2V1X2ludGVncmF0ZWRAbWV0YS5kYXRhJGxpYnJhcnkpCnNldC5zZWVkKDEyMykKc2VsZWN0ZWRfY2VsbHMgPSByb3duYW1lcyhzZXVfaW50ZWdyYXRlZEBtZXRhLmRhdGEpW3NldV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSRsaWJyYXJ5PT0iV1QgMSJdW3NhbXBsZSgxOmNvdW50c19wZXJfbGlicmFyeVsiV1QgMSJdLCAxMDAwKV0KCnNlbGVjdGVkX2NlbGxzID0gYyhzZWxlY3RlZF9jZWxscywgcm93bmFtZXMoc2V1X2ludGVncmF0ZWRAbWV0YS5kYXRhKVtzZXVfaW50ZWdyYXRlZEBtZXRhLmRhdGEkbGlicmFyeT09IldUIDIiXVtzYW1wbGUoMTpjb3VudHNfcGVyX2xpYnJhcnlbIldUIDIiXSwgMTAwMCldKQoKc2VsZWN0ZWRfY2VsbHMgPSBjKHNlbGVjdGVkX2NlbGxzLCByb3duYW1lcyhzZXVfaW50ZWdyYXRlZEBtZXRhLmRhdGEpW3NldV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSRsaWJyYXJ5PT0iSE9NIEtPIDEiXVtzYW1wbGUoMTpjb3VudHNfcGVyX2xpYnJhcnlbIkhPTSBLTyAxIl0sIDEwMDApXSkKCnNlbGVjdGVkX2NlbGxzID0gYyhzZWxlY3RlZF9jZWxscywgcm93bmFtZXMoc2V1X2ludGVncmF0ZWRAbWV0YS5kYXRhKVtzZXVfaW50ZWdyYXRlZEBtZXRhLmRhdGEkbGlicmFyeT09IkhPTSBLTyAyIl1bc2FtcGxlKDE6Y291bnRzX3Blcl9saWJyYXJ5WyJIT00gS08gMiJdLCAxMDAwKV0pCgoKCnNldV9pbnRlZ3JhdGVkX3RlbXAgPSBzdWJzZXQoc2V1X2ludGVncmF0ZWQsIGNlbGxzPXNlbGVjdGVkX2NlbGxzKQoKcCA9IFVNQVBQbG90KHNldV9pbnRlZ3JhdGVkX3RlbXAsZ3JvdXAuYnk9ImxpYnJhcnkiKQpwID0gdGhlbWVfcHVibGljYXRpb25fcGxvdChwLCAiTGlicmFyeSIpCnByaW50KHApCmNvd3Bsb3Q6OnNhdmVfcGxvdChwbG90PXAsCiAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9IHBhc3RlMChmaWxlcHJlZml4LCAic3VwcGxmaWdfdW1hcF9vdmVybGF5X2xpYnJhcnlfZG93bnNhbXBsZWQxMDAwLnBkZiIpLAogICAgICAgICAgICAgICAgICAgYmFzZV9oZWlnaHQ9NCwKICAgICAgICAgICAgICAgICAgIGJhc2Vfd2lkdGg9NikKYGBgCkRvd25zYW1wbGVkIHBsb3QgdG8gc2hvdyBhbiBlcXVhbCBudW1iZXIgb2YgY2VsbHMgcGVyIGxpYnJhcnkgLSAxNTAwIGNlbGxzCmBgYHtyfQpjb3VudHNfcGVyX2xpYnJhcnkgPSB0YWJsZShzZXVfaW50ZWdyYXRlZEBtZXRhLmRhdGEkbGlicmFyeSkKc2V0LnNlZWQoMTIzKQpzZWxlY3RlZF9jZWxscyA9IHJvd25hbWVzKHNldV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSlbc2V1X2ludGVncmF0ZWRAbWV0YS5kYXRhJGxpYnJhcnk9PSJXVCAxIl1bc2FtcGxlKDE6Y291bnRzX3Blcl9saWJyYXJ5WyJXVCAxIl0sIDE1MDApXQoKc2VsZWN0ZWRfY2VsbHMgPSBjKHNlbGVjdGVkX2NlbGxzLCByb3duYW1lcyhzZXVfaW50ZWdyYXRlZEBtZXRhLmRhdGEpW3NldV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSRsaWJyYXJ5PT0iV1QgMiJdW3NhbXBsZSgxOmNvdW50c19wZXJfbGlicmFyeVsiV1QgMiJdLCAxNTAwKV0pCgpzZWxlY3RlZF9jZWxscyA9IGMoc2VsZWN0ZWRfY2VsbHMsIHJvd25hbWVzKHNldV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSlbc2V1X2ludGVncmF0ZWRAbWV0YS5kYXRhJGxpYnJhcnk9PSJIT00gS08gMSJdW3NhbXBsZSgxOmNvdW50c19wZXJfbGlicmFyeVsiSE9NIEtPIDEiXSwgMTUwMCldKQoKc2VsZWN0ZWRfY2VsbHMgPSBjKHNlbGVjdGVkX2NlbGxzLCByb3duYW1lcyhzZXVfaW50ZWdyYXRlZEBtZXRhLmRhdGEpW3NldV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSRsaWJyYXJ5PT0iSE9NIEtPIDIiXVtzYW1wbGUoMTpjb3VudHNfcGVyX2xpYnJhcnlbIkhPTSBLTyAyIl0sIDE1MDApXSkKCgoKc2V1X2ludGVncmF0ZWRfdGVtcCA9IHN1YnNldChzZXVfaW50ZWdyYXRlZCwgY2VsbHM9c2VsZWN0ZWRfY2VsbHMpCgpwID0gVU1BUFBsb3Qoc2V1X2ludGVncmF0ZWRfdGVtcCxncm91cC5ieT0ibGlicmFyeSIpCnAgPSB0aGVtZV9wdWJsaWNhdGlvbl9wbG90KHAsICJMaWJyYXJ5IikKcHJpbnQocCkKY293cGxvdDo6c2F2ZV9wbG90KHBsb3Q9cCwKICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gcGFzdGUwKGZpbGVwcmVmaXgsICJzdXBwbGZpZ191bWFwX292ZXJsYXlfbGlicmFyeV9kb3duc2FtcGxlZDE1MDAucGRmIiksCiAgICAgICAgICAgICAgICAgICBiYXNlX2hlaWdodD00LAogICAgICAgICAgICAgICAgICAgYmFzZV93aWR0aD02KQpgYGAKU2F2ZSB0aGUgb3V0Y29tZQpgYGB7cn0KIyBzYXZlIHRoZSBLcnQ0IGVzdGltYXRlcwptZXRhZGF0ID0gc2V1X2tlcmFfaW50ZWdyYXRlZEBtZXRhLmRhdGEKbWV0YWRhdCRjZWxsID0gcm93bmFtZXMobWV0YWRhdCkKCm1ldGFkYXQgPSBtZXRhZGF0WywgYygiY2VsbCIsICJvcmlnLmlkZW50IiwgIlBoYXNlIiwgInByb3AubXQiLCAibkNvdW50X1JOQSIsICJuRmVhdHVyZV9STkEiKV0KY29sbmFtZXMobWV0YWRhdClbMl0gPSAibGlicmFyeSIKCm1ldGFkYXQkaXNfYmFzYWwgPSBwYzEkbGF5ZXI9PSJCYXNhbCIKd3JpdGUudGFibGUobWV0YWRhdCwgZmlsZT1wYXN0ZTAoZmlsZXByZWZpeCwgIm5vdGNoMV9iYXNhbF9zdXByYWJhc2FsX2NhbGxzX3VtYXBfdGhyZXNob2xkLnR4dCIpLCBxdW90ZT1GLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUYpCmBgYAoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgo=